我正在开发一个TCP套接字相关的应用程序,其中我创建的对象引用了一个System.Net.Sockets.Socket
对象。后一个对象似乎变成了null
,为了理解为什么,我想检查一下我自己的对象是否被重新创建。为此,我想到了最简单的方法,即检查this
的内存地址。但是,当将此添加到监视窗口时,我得到以下错误消息:
Name Value
&this error CS0211: Cannot take the address of the given expression
因为在c#中检查对象的内存地址似乎是不可能的,我怎么能在调试代码时验证我正在处理相同或另一个对象?
在c#中,对象在垃圾收集期间被移动。您不能简单地获取它的地址,因为地址在GC堆压缩时发生了变化。
在c#中处理指针需要不安全的代码,你离开了安全代码的领域,基本上使它和c++一样不安全。
你可以使用像windbg这样的调试器,它可以显示对象的内存地址——但是当GC移动它们时,它们仍然会改变。
如果你想看看你的类的新实例是否被创建,为什么不在构造函数中设置一个断点?
我相信@thomas的回答。
你可以添加一个唯一的标识符(比如GUID)属性到你的对象,并使用它来确定你是否有相同的对象。
您可以重写Equals
方法来比较两个对象,如果它们与下面相同。
public class MyClass
{
public Guid Id { get; } = Guid.NewGuid();
public override bool Equals(object obj)
{
return obj is MyClass second && this.Id == second.Id;
}
}
如前所述,对象的地址不是一种可行的方法来推断像DotNet这样的垃圾收集虚拟机中的对象。在DotNet中,如果您使用fixed
关键字、unsafe
块或GCHandle.Alloc()
,您可能有机会观察对象的地址,但这些都非常糟糕,它们将对象固定在内存中,因此它们不能被垃圾收集,这是您绝对不想要的。一旦你解除了一个对象的固定,那么它的地址就可以自由地改变了,所以你不能跟踪它。
幸运的是,您不需要这些!
你不需要地址,因为你只需要一个助记符,以便在故障排除过程中识别该对象。为此,您有以下选项:
-
创建一个发出唯一id的单例,并在每个对象的构造函数中调用该单例来获得唯一id,将id存储在对象中,并将id包含在对象的
ToString()
方法中,或者在您可能用于调试显示的任何其他方法中。 -
使用
System.Runtime.Serialization.ObjectIDGenerator
类,它或多或少地做了单例id生成器会做的事情,但以一种更高级,可能更容易使用的方式。(我没有使用它的个人经验,所以我不能给出更多的建议。) -
使用
System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode( object )
方法,它返回在其他圈子中已知的身份哈希码对象的。它保证在对象的整个生命周期中保持不变,但不保证在所有对象中是唯一的。然而,由于它是32位长,在另一个对象碰巧发出相同的哈希码之前,它将是地狱里的寒冷的一天,所以它将很好地满足您的所有故障排除目的。
帮你自己一个忙,用十六进制显示你的对象的身份哈希码;该数字将比十进制更短,并且有更多的数字种类,因此在故障排除时更容易在短期记忆中保留。