我有一个前一段时间写的函数(为.NET3.5),现在我已经升级到4.0
我不能让它工作。
函数为:
public static class MemoryAddress
{
public static string Get(object a)
{
GCHandle handle = GCHandle.Alloc(a, GCHandleType.Pinned);
IntPtr pointer = GCHandle.ToIntPtr(handle);
handle.Free();
return "0x" + pointer.ToString("X");
}
}
字符串
现在,当我调用它- MemoryAddress.Get(new Car(“blue”))时,
public class Car
{
public string Color;
public Car(string color)
{
Color = color;
}
}
型
我得到的错误:
对象包含非基元数据或不可直接复制到原结构中的数据。
为什么不管用了?
我现在如何获得托管对象的内存地址?
9条答案
按热度按时间b0zn9rqh1#
你可以使用GCHandleType.Weak而不是Pinned。另一方面,还有另一种方法可以获得指向对象的指针:
字符串
需要不安全的块,是非常,非常危险的,不应该使用。
在C#中不可能使用by-ref局部变量的时候,有一种没有文档的机制可以完成类似的事情-
__makeref
。型
有一个重要的区别是 TypedReference 是“泛型”的;它可以用来存储对任何类型的变量的引用。如果这样的引用需要指定其类型,例如
__refvalue(tr, object)
,如果不匹配,则抛出异常。为了实现类型检查,TypedReference 必须有两个字段,一个是变量的实际地址,另一个是指向其类型表示的指针。碰巧地址是第一个字段。
因此,
__makeref
首先用于获取对变量o
的引用。强制转换(IntPtr**)(&tr)
将结构视为数组(通过指针表示)IntPtr*
(指向通用指针类型的指针),通过指向它的指针访问。指针首先被解引用以获得第一字段,然后指针再次被解引用以获得实际存储在变量o
中的值-指向对象本身的指针。2012年,我提出了一个更好、更安全的解决方案:
型
这创建了一个动态方法,该方法首先固定对象(因此其存储不会在托管堆中移动),然后执行一个接收其地址的委托。在委托执行期间,对象仍然被固定,因此可以安全地通过指针进行操作:
型
这是最简单的固定对象的方法,因为 GCHandle 要求类型是可复制的,以便固定它。它的优点是不使用实现细节,未记录的关键字和内存黑客。
68de4m5k2#
您应该调用
GetHashCode()
而不是这段代码,它将为每个示例返回一个(希望是-)唯一的值。您也可以使用
ObjectIDGenerator
class,它保证是唯一的。lsmepo6l3#
如果你不需要内存地址,而是需要一些唯一标识托管对象的方法,那么有一个更好的解决方案:
字符串
这是线程安全的,并且在内部使用弱引用,所以你不会有内存泄漏。
你可以使用任何你喜欢的密钥生成方法,我在这里使用
Guid.NewGuid()
是因为它简单而且线程安全。更新
我继续创建了一个Nuget包Overby.Extensions.Attachments,其中包含一些用于将对象附加到其他对象的扩展方法。有一个名为
GetReferenceId()
的扩展可以有效地执行此答案中的代码。qmb5sa224#
当你释放句柄时,垃圾收集器就可以自由地移动被固定的内存。如果你有一个指向内存的指针,而你又不固定该内存,那么所有的赌注都被取消了。这在Python 3.5中工作可能只是运气好。JIT编译器和Python 4.0的运行时可能在对象生存期分析方面做得更好。
如果你真的想这样做,你可以使用
try/finally
来防止对象被取消固定,直到你使用它:字符串
n1bvdmb65#
这里有一个我想到的简单方法,它不涉及不安全的代码或固定对象。也可以反向工作(从地址的对象):
字符串
sg3maiej6#
这对我来说很有效...
字符串
ef1yzkbh7#
切换分配类型:
字符串
1mrurvl18#
对.net core
字符串
ruoxqz4g9#
在.NET中获取任意对象的地址是不可能的,但如果您更改源代码并使用mono,则可以完成。请参阅此处的说明:获取.NET对象的内存地址(C#)