Peringatan: Pertanyaan ini agak sesat ... programmer agama selalu taat pada praktik yang baik, tolong jangan membacanya. :)
Adakah yang tahu mengapa menggunakan TypedReference sangat tidak disarankan (secara implisit, karena kurangnya dokumentasi)?
Saya telah menemukan kegunaan besar untuk itu, seperti ketika melewati parameter generik melalui fungsi yang seharusnya tidak generik (saat menggunakan object
mungkin berlebihan atau lambat, jika Anda membutuhkan tipe nilai), untuk saat Anda membutuhkan pointer buram, atau ketika Anda perlu mengakses elemen array dengan cepat, yang spesifikasinya Anda temukan saat runtime (menggunakan Array.InternalGetReference
). Karena CLR bahkan tidak mengizinkan penggunaan yang salah dari jenis ini, mengapa itu tidak disarankan? Sepertinya tidak aman atau apa pun ...
Kegunaan lain yang saya temukan untuk TypedReference
:
"Mengkhususkan" obat generik dalam C # (ini aman untuk jenis):
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); }
}
Menulis kode yang berfungsi dengan pointer umum (ini sangat tidak aman jika disalahgunakan, tetapi cepat dan aman jika digunakan dengan benar):
//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);
}
Menulis versi metodesizeof
instruksi, yang kadang-kadang berguna:
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)); }
}
}
Menulis metode yang melewati parameter "status" yang ingin menghindari tinju:
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*/ }
}
Jadi mengapa penggunaan seperti ini "tidak disarankan" (karena kurangnya dokumentasi)? Adakah alasan keamanan tertentu? Tampaknya sangat aman dan dapat diverifikasi jika tidak dicampur dengan pointer (yang sebenarnya tidak aman atau dapat diverifikasi) ...
Memperbarui:
Kode contoh untuk menunjukkan bahwa, memang, TypedReference
bisa dua kali lebih cepat (atau lebih):
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
}
}
(Sunting: Saya mengedit patokan di atas, karena versi terakhir dari pos menggunakan versi debug kode [saya lupa mengubahnya untuk melepaskan], dan tidak memberi tekanan pada GC. Versi ini sedikit lebih realistis, dan pada sistem saya, ini lebih dari tiga kali lebih cepat dengan TypedReference
rata-rata.)
int
-> DockStyle
). Kotak ini nyata, dan hampir sepuluh kali lebih lambat.
TypedReference: 203 ticks
,boxing/unboxing: 31 ticks
. Tidak peduli apa yang saya coba (termasuk berbagai cara untuk melakukan penghitungan waktu) tinju / unboxing masih lebih cepat di sistem saya.