Попередження: Це питання єретично ... релігійні програмісти завжди дотримуються належної практики, не читайте цього. :)
Хтось знає, чому використання TypedReference настільки відмовляється (неявно, через відсутність документації)?
Я знайшов для цього велику користь, наприклад, при передачі загальних параметрів через функції, які не повинні бути загальними (коли використання objectможе бути надмірним або повільним, якщо вам потрібен тип значення), коли вам потрібен непрозорий вказівник, або коли вам потрібно швидко отримати доступ до елемента масиву, специфікації якого ви знайдете під час виконання (за допомогою Array.InternalGetReference). Оскільки CLR навіть не допускає неправильного використання цього типу, чому це не рекомендується? Це не здається небезпечним чи нічого ...
Інші види використання, які я знайшов для TypedReference:
"Спеціалізовані" дженерики на 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)); }
}
}
Написання методу, який передає параметр "state", який хоче уникнути боксу:
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. Незалежно від того, що я намагаюся (включаючи різні способи зробити таймінги), бокс / розблокування все ще швидше в моїй системі.