为什么这两个比较结果不同?


68

为什么此代码返回true:

new Byte() == new Byte()   // returns true

但是此代码返回false:

new Byte[0] == new Byte[0] // returns false


4
我很惊讶,没有人发现这个问题重复,因为这是非常基本的类型与引用类型问题。
Ganesh Jadhav 2014年

2
我对它获得的最多投票数感到惊讶(截至目前,共有51个)。
Ganesh Jadhav 2014年

Answers:



43

字节是.NET中的值类型,这意味着==当且仅当两个字节具有相同的值时,运算符才返回true。这也称为价值平等

但是数组是.NET中的引用类型,这意味着==运算符仅当在内存中引用相同的数组实例时才返回true。这也称为引用相等标识

注意,==引用类型和值类型都可以重载运算符。System.String例如,是引用类型,但是==字符串的运算符按顺序比较数组中的每个字符。请参见重载Equals()和运算符==的准则(C#编程指南)

如果要测试数组是否包含完全相同的值(按顺序),则应考虑使用Enumerable.SequenceEqual代替==


1
我认为问题的症结在于==运营商及其双重性质。这个答案显然涵盖了这一点。
rory.ap 2014年

我喜欢将“默认情况下”用于其他引用类型,但是实际上是否可以更改数组类型的这种行为?
克里斯·海斯

@ChrisHayes否。只能在为其定义的类中重载运算符,并且由于System.Array该类不提供重载,因此它使用默认的引用相等性。您可能会认为可以创建自己的数组类型(System.Array毕竟是抽象的),但是编译器不允许您从其继承。不过,您可能会巧妙地使用隐式运算符来将数组转换为另一种类型,这可能会相当接近,但是这个主意使我的皮肤难以捉摸。
pswg

10

比较引用实际上是比较指针地址,这是不同的,这就是返回false和在值地址中返回值的原因,无论它是否比较值。

编译器尝试将值类型存储在寄存器中,但由于寄存器数量有限,当引用类型在堆栈中但值保留堆中的内存地址时,值[Reference]将在堆栈中进一步存储。

这里的比较比较堆栈中存在的值,在第一种情况下两者都是相同的,而在第二种情况下则是堆的地址不同。

参考


5
这是一个非常令人困惑的答案。第一部分仍然使它看起来像是参考比较,因为您仍然使用“指针”一词。图形和纯文本的使用也很烦人,因为它使我很难对其进行编辑以改善答案。
克里斯·海斯

-1代表了“将值类型存储在堆栈中”的神话。我以为这两个new Byte()调用的结果很可能存储在寄存器中。
Damien_The_Unbeliever 2014年

@Damien_The_Unbeliever寄存器存储取决于寄存器的可用性,否则它存储在堆栈中,在两种情况下,值都是相同的。
Zaheer Ahmed 2014年


5
您的整个答案仍然是一个无聊的旅行。值类型的关键方面是按值比较它们。该值的存储位置无关紧要。您可以将两种值类型放置到堆分配的结构中(有意或通过提升),并且比较仍将基于它们的值。
Damien_The_Unbeliever 2014年

1

==运算符有一个重载,其中两个操作数都是类型byte,并且实现为比较每个字节的值。在这种情况下,您有两个零字节,它们相等。

==操作者不超载为阵列,以便具有两个过载object时使用的操作数(因为数组是类型的object)在第二种情况下,其实现比较两个对象的引用。对这两个数组的引用是不同的。

值得注意的是,这与(直接)与byte值类型和数组为引用类型无关。==for的运算符byte具有值语义,因为该实现对该运算符有特定的重载。如果超载不存在那么就可以无过载为两个字节将是有效的操作数,这样的代码不会在所有的编译。通过创建一个自定义并将其两个实例与运算符进行比较,您可以轻松地看到它。除非您为这些类型提供自己的实现,否则代码不会编译。struct====

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.