您可以使用GCHandleType.Weak代替Pinned。另一方面,还有一种获取对象指针的方法:
object o = new object();
TypedReference tr = __makeref(o);
IntPtr ptr = **(IntPtr**)(&tr);
需要使用unsafe块,非常非常危险,不应该使用。☺
在C#中无法使用按引用传递的本地变量之前,有一种未记录的机制可以实现类似的功能 - __makeref
。
object o = new object();
ref object r = ref o;
//roughly equivalent to
TypedReference tr = __makeref(o);
这里有一个重要的差别,TypedReference是"泛型"的,可以用来存储任何类型的变量的引用。访问这样的引用需要指定其类型,例如__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)); //the first pointer in the managed object header in .NET points to its run-time type info
这是最简单的固定对象的方法,因为 GCHandle 要求类型可平坦化才能将其固定。它的优点是不使用实现细节、未记录的关键字和内存黑客攻击。
object.ReferenceEquals
方法。它并不会告诉你引用是什么,但它会返回一个布尔值,指示这两个对象是否指向同一堆位置。(希望这能帮助到某些人。) - BrainSlugs83GCHandle.ToIntPtr
返回的是句柄本身的内部表示,而不是它所指向对象的地址。如果你为同一个对象创建多个句柄,GCHandle.ToIntPtr
将为每个句柄返回不同的结果。GCHandle.AddrOfPinnedObject
返回的是句柄所指向对象的地址。有关更多详细信息,请参见GCHandle.ToIntPtr vs. GCHandle.AddrOfPinnedObject。 - Antosha