Pinned 대신 GCHandleType.Weak을 사용할 수 있습니다. 반면에 객체에 대한 포인터를 얻는 또 다른 방법이 있습니다.
object o = new object();
TypedReference tr = __makeref(o);
IntPtr ptr = **(IntPtr**)(&tr);
안전하지 않은 블록이 필요하며 매우 위험하므로 전혀 사용해서는 안됩니다. ☺
C #에서 by-ref 로컬을 사용할 수 없었던 당시에는 유사한 작업을 수행 할 수있는 문서화되지 않은 메커니즘이 하나있었습니다 __makeref.
object o = new object();
ref object r = ref o;
TypedReference tr = __makeref(o);
TypedReference 가 "generic" 이라는 점에는 한 가지 중요한 차이점이 있습니다 . 모든 유형의 변수에 대한 참조를 저장하는 데 사용할 수 있습니다. 이러한 참조에 액세스하려면 해당 유형 (예 :)을 지정해야하며 __refvalue(tr, object)일치하지 않으면 예외가 발생합니다.
유형 검사를 구현하려면 TypedReference에 두 개의 필드가 있어야합니다. 하나는 변수에 대한 실제 주소가 있고 다른 하나는 해당 유형 표현에 대한 포인터가 있습니다. 주소가 첫 번째 필드 인 경우에도 마찬가지입니다.
따라서 __makeref는 변수에 대한 참조를 얻기 위해 먼저 사용됩니다 o. 캐스트 (IntPtr**)(&tr)는 구조를 IntPtr*포인터를 통해 액세스되는 (일반 포인터 유형에 대한 포인터)의 배열 ( 포인터를 통해 표시됨)로 처리합니다. 포인터는 첫 번째 필드를 얻기 위해 먼저 역 참조되고, 그 다음에는 변수에 실제로 저장된 값 ( o객체 자체에 대한 포인터)을 얻기 위해 포인터가 다시 역 참조 됩니다.
그러나 2012 년부터 더 좋고 안전한 솔루션을 찾았습니다.
public static class ReferenceHelpers
{
public static readonly Action<object, Action<IntPtr>> GetPinnedPtr;
static ReferenceHelpers()
{
var dyn = new DynamicMethod("GetPinnedPtr", typeof(void), new[] { typeof(object), typeof(Action<IntPtr>) }, typeof(ReferenceHelpers).Module);
var il = dyn.GetILGenerator();
il.DeclareLocal(typeof(object), true);
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Stloc_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Ldloc_0);
il.Emit(OpCodes.Conv_I);
il.Emit(OpCodes.Call, typeof(Action<IntPtr>).GetMethod("Invoke"));
il.Emit(OpCodes.Ret);
GetPinnedPtr = (Action<object, Action<IntPtr>>)dyn.CreateDelegate(typeof(Action<object, Action<IntPtr>>));
}
}
이렇게하면 먼저 개체를 고정한 다음 (관리되는 힙에서 저장소가 이동하지 않도록) 해당 주소를받는 대리자를 실행하는 동적 메서드가 생성됩니다. 대리자를 실행하는 동안 개체는 여전히 고정되어 있으므로 포인터를 통해 안전하게 조작 할 수 있습니다.
object o = new object();
ReferenceHelpers.GetPinnedPtr(o, ptr => Console.WriteLine(Marshal.ReadIntPtr(ptr) == typeof(object).TypeHandle.Value));
GCHandle 은 개체 를 고정하기 위해 유형을 blittable로 설정해야하므로 개체를 고정하는 가장 쉬운 방법 입니다. 구현 세부 사항, 문서화되지 않은 키워드 및 메모리 해킹을 사용하지 않는 이점이 있습니다.