คำเตือน: คำถามนี้เป็นเรื่องนอกรีตนักเขียนโปรแกรมทางศาสนาปฏิบัติตามแนวปฏิบัติที่ดีเสมอโปรดอย่าอ่าน :)
มีใครรู้บ้างไหมว่าทำไมการใช้TypedReferenceจึงเป็นสิ่งที่ทำให้หมดกำลังใจ (โดยปริยายโดยไม่มีเอกสารประกอบ)
ฉันพบการใช้งานที่ยอดเยี่ยมสำหรับมันเช่นเมื่อส่งพารามิเตอร์ทั่วไปผ่านฟังก์ชั่นที่ไม่ควรเป็นแบบทั่วไป (เมื่อใช้object
อาจจะ overkill หรือช้าถ้าคุณต้องการประเภทค่า) สำหรับเมื่อคุณต้องการตัวชี้ทึบแสงหรือ สำหรับเมื่อคุณต้องการเข้าถึงองค์ประกอบของอาร์เรย์ได้อย่างรวดเร็วซึ่งมีรายละเอียดที่คุณพบที่รันไทม์ (โดยใช้Array.InternalGetReference
) เนื่องจาก CLR ไม่อนุญาตการใช้งานประเภทนี้อย่างไม่ถูกต้องทำไมจึงหมดกำลังใจ ดูเหมือนจะไม่ปลอดภัยหรืออะไร ...
การใช้งานอื่น ๆ ที่ฉันได้พบTypedReference
:
Generic "เชี่ยวชาญ" ใน C # (นี่คือประเภทที่ปลอดภัย):
static void foo<T>(ref T value)
{
//This is the ONLY way to treat value as int, without boxing/unboxing objects
if (value is int)
{ __refvalue(__makeref(value), int) = 1; }
else { value = default(T); }
}
เขียนโค้ดที่ทำงานร่วมกับตัวชี้ทั่วไป (นี้เป็นอย่างมากที่ไม่ปลอดภัยถ้าในทางที่ผิด แต่อย่างรวดเร็วและปลอดภัยหากใช้อย่างถูกต้อง):
//This bypasses the restriction that you can't have a pointer to T,
//letting you write very high-performance generic code.
//It's dangerous if you don't know what you're doing, but very worth if you do.
static T Read<T>(IntPtr address)
{
var obj = default(T);
var tr = __makeref(obj);
//This is equivalent to shooting yourself in the foot
//but it's the only high-perf solution in some cases
//it sets the first field of the TypedReference (which is a pointer)
//to the address you give it, then it dereferences the value.
//Better be 10000% sure that your type T is unmanaged/blittable...
unsafe { *(IntPtr*)(&tr) = address; }
return __refvalue(tr, T);
}
การเขียนเวอร์ชันวิธีการsizeof
สอนซึ่งอาจมีประโยชน์ในบางครั้ง:
static class ArrayOfTwoElements<T> { static readonly Value = new T[2]; }
static uint SizeOf<T>()
{
unsafe
{
TypedReference
elem1 = __makeref(ArrayOfTwoElements<T>.Value[0] ),
elem2 = __makeref(ArrayOfTwoElements<T>.Value[1] );
unsafe
{ return (uint)((byte*)*(IntPtr*)(&elem2) - (byte*)*(IntPtr*)(&elem1)); }
}
}
การเขียนวิธีการที่ผ่านพารามิเตอร์ "รัฐ" ที่ต้องการหลีกเลี่ยงการชกมวย:
static void call(Action<int, TypedReference> action, TypedReference state)
{
//Note: I could've said "object" instead of "TypedReference",
//but if I had, then the user would've had to box any value types
try
{
action(0, state);
}
finally { /*Do any cleanup needed*/ }
}
เหตุใดจึงมีการใช้งานเช่นนี้ "หมดกำลังใจ" (โดยไม่มีเอกสารประกอบ) เหตุผลด้านความปลอดภัยโดยเฉพาะ? ดูเหมือนว่าจะปลอดภัยและตรวจสอบได้อย่างสมบูรณ์หากไม่ได้ผสมกับพอยน์เตอร์ (ซึ่งไม่ปลอดภัยหรือตรวจสอบได้)
ปรับปรุง:
โค้ดตัวอย่างเพื่อแสดงให้เห็นว่าTypedReference
สามารถเร็วเป็นสองเท่า (หรือมากกว่า):
using System;
using System.Collections.Generic;
static class Program
{
static void Set1<T>(T[] a, int i, int v)
{ __refvalue(__makeref(a[i]), int) = v; }
static void Set2<T>(T[] a, int i, int v)
{ a[i] = (T)(object)v; }
static void Main(string[] args)
{
var root = new List<object>();
var rand = new Random();
for (int i = 0; i < 1024; i++)
{ root.Add(new byte[rand.Next(1024 * 64)]); }
//The above code is to put just a bit of pressure on the GC
var arr = new int[5];
int start;
const int COUNT = 40000000;
start = Environment.TickCount;
for (int i = 0; i < COUNT; i++)
{ Set1(arr, 0, i); }
Console.WriteLine("Using TypedReference: {0} ticks",
Environment.TickCount - start);
start = Environment.TickCount;
for (int i = 0; i < COUNT; i++)
{ Set2(arr, 0, i); }
Console.WriteLine("Using boxing/unboxing: {0} ticks",
Environment.TickCount - start);
//Output Using TypedReference: 156 ticks
//Output Using boxing/unboxing: 484 ticks
}
}
(แก้ไข: ฉันแก้ไขเกณฑ์มาตรฐานข้างต้นเนื่องจากเวอร์ชันล่าสุดของโพสต์ใช้เวอร์ชันการแก้ไขข้อบกพร่องของรหัส [ฉันลืมเปลี่ยนเป็นรุ่น] และไม่กดดัน GC รุ่นนี้เป็นจริงมากขึ้นและ ในระบบของฉันมันเร็วกว่าสามเท่าTypedReference
โดยเฉลี่ย)
int
-> DockStyle
) กล่องนี้เป็นของจริงและช้าลงเกือบสิบเท่า
TypedReference: 203 ticks
,boxing/unboxing: 31 ticks
. ไม่ว่าสิ่งที่ฉันลอง (รวมถึงวิธีที่แตกต่างกันในการทำเวลา) มวย / unboxing ยังคงเร็วขึ้นในระบบของฉัน