경고 :이 질문은 약간 이단 적입니다 ... 종교적 프로그래머들은 항상 좋은 관행을 따르고 있습니다. :)
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
평균 3 배 이상 빠릅니다 .)
int
-> DockStyle
)으로 캐스팅합니다 . 이 상자는 실제에 적용되며 거의 10 배 느립니다.
TypedReference: 203 ticks
,boxing/unboxing: 31 ticks
. 내가 시도하는 (시간을 측정하는 다른 방법을 포함하여) 시도하거나 관계없이 권투 / 언 박싱은 여전히 시스템에서 더 빠릅니다.