C#中Python的id()等价物是什么?

5 投票
5 回答
854 浏览
提问于 2025-04-16 08:17

有没有C#类似于Python的id()的功能?如果没有,我该如何测试一个对象是否发生了变化?比如,在Python中,有一个不可变的对象:

>>> s = "abc"
>>> id(s)
11172320
>>> s += "d"
>>> id(s)
18632928

还有一个可变的对象:

>>> a = [1, 2, 3]
>>> id(a)
11259656
>>> a += [4]
>>> id(a)
11259656

编辑 - 到目前为止,大部分回答都是关于比较函数/运算符的,但我更关心的是被比较的值。我最开始没有说清楚这一点,现在我接受Object.ReferenceEquals作为答案。

5 个回答

4

我觉得你问的问题不太对。我的回答虽然详细,但这里给你个简单的总结:

  • C#中没有和id完全相同的东西。
  • 你可以通过指针在C#中获取对象的地址,但不建议这样做来解决你的问题。
  • 方法object.ReferenceEquals做的事情和你想做的有点相似。
  • 你想解决的问题在C#中并不存在,和Python的情况不一样。
  • 你在Python中使用id的方式在这里也不能解决问题。

首先,我会引用一下关于id文档,因为大多数人似乎没有看过。请一定要阅读这个,因为我后面的回答只有在你知道id的作用时才有意义(而且,它并不是用来生成哈希码的):

返回一个对象的“身份”。这是一个整数(或长整数),在对象的生命周期内是唯一且不变的。两个生命周期不重叠的对象可能会有相同的id()值。

CPython实现细节:这是对象的地址。

C#没有和id相同的东西,但可以通过在不安全模式下获取对象的指针来得到对象的地址。

unsafe
{
     IntPtr x = (IntPtr)(&v);
     Console.WriteLine(x);
}

不过你不应该这样做。在C#中,你很少需要使用不安全的代码,如果你这样做,调用你代码的其他代码也会变得不安全。还有其他方法可以解决你的问题,不需要用到不安全的代码。此外,在.NET中(和Python不同),垃圾回收器会在内存中移动对象,所以如果你想要一个在对象整个生命周期内保持不变的身份,内存地址对你没有用。你应该创建一个只读属性,比如叫Id


在C#中,还有一种不使用不安全代码的方法是通过Object.ReferenceEquals来测试两个变量是否引用同一个对象。不过你可能也不应该这样做。C#没有像Python那样的模糊性。要把一个变量指向一个新对象,你使用=运算符。调用一个方法几乎总是不会改变引用,所以你不需要担心。在C#中,x += y;几乎等同于x = x + y,而且你不能像在Python中那样改变它。


另外,正如文档中提到的,你的Python示例是有问题的——id不能可靠地用来测试一个对象是否发生了变化。举个例子:

>>> s='abc'
>>> id(s)
37822208
>>> s+='d'
>>> id(s)
61711680    # Changes
>>> s+='e'
>>> id(s)
61711680    # Oops... doesn't change even though the value changed.

在Python中,如果你需要比较两个对象以查看它们是否是同一个对象(而且你可能不应该这样做),那么使用is,它是Python中object.ReferenceEquals的等价物。id只保证在对象的生命周期内是常量。如上所示,id保证不同对象的id是不同的。特别是如果其中一个对象不再存在,你可能会(并且有时会)因为内存的重用而得到相同的id。因此,在这里使用id真的是个坏主意。

总结

别这么做。

5

你可以用 Object.ReferenceEquals 来测试对象的身份。

4

你可以使用 Object.ReferenceEquals() 这个函数:

这个函数用来判断指定的对象实例是否是同一个实例。

借用一下你提到的Python例子:

List<int> listA = new List<int> { 1, 2, 3 };
List<int> listB = listA;
listA.Add(4);

bool equal = object.ReferenceEquals(listA, listB);

equal 的值会是 true

撰写回答