简短的答案:一致性
不过,为了正确回答您的问题,我建议我们退后一步,探讨一下编程语言中相等意味着什么的问题。至少存在三种不同的可能性,它们以各种语言使用:
- 引用相等:意味着如果a和b引用相同的对象,则a = b为true。即使a和b引用了不同的对象,即使a和b的所有属性都相同,也不是正确的。
- 浅相等:意味着如果a和b引用的对象的所有属性都相同,则a = b为true。通过对代表两个对象的内存空间进行按位比较,可以轻松实现浅相等。请注意,引用相等意味着浅层相等
- 深度相等:表示如果a和b中的每个属性相同或高度相等,则a = b为真。请注意,引用相等和浅层相等都暗示着深度相等。从这个意义上说,深层平等是最弱的平等形式,而参照平等是最强的平等形式。
经常使用这三种类型的相等性,因为它们易于实现:编译器可以轻松生成所有三种相等性检查(在深度相等的情况下,如果结构要被比较具有循环引用)。但是还有另一个问题:这些都不适合。
在非平凡的系统中,对象的相等性通常定义为深度和引用相等性之间的某种关系。为了检查我们是否希望在特定上下文中将两个对象视为相等,我们可能需要通过比较它们在内存中的位置来比较某些属性,并通过深度相等来比较其他属性,而某些属性可能被允许完全不同。我们真正想要的是一种“第四种平等”,这是一种非常好的,在文献中经常被称为语义平等。在我们的领域中,如果事物相等,则事物相等。=)
所以我们可以回到您的问题:
我根本没有想到的默认设置是否有一些主要好处,或者默认行为应该是逻辑相等,并且如果该类不存在逻辑相等,则默认返回引用相等是否合理?
用任何语言写“ a == b”是什么意思?理想情况下,它应该始终是相同的:语义平等。但这是不可能的。
主要考虑因素之一是,至少对于简单的类型(如数字),我们期望分配相同值后两个变量相等。见下文:
var a = 1;
var b = a;
if (a == b){
...
}
a = 3;
b = 3;
if (a == b) {
...
}
在这种情况下,我们期望两个语句中的“ a等于b”。其他任何事情都会发疯。大多数(如果不是全部)语言都遵循此约定。因此,通过简单的类型(又称值),我们知道如何实现语义相等。对于对象,这可能是完全不同的东西。见下文:
var a = new Something(1);
var b = a;
if (a == b){
...
}
b = new Something(1);
a.DoSomething();
b.DoSomething();
if (a == b) {
...
}
我们希望第一个“如果”始终为真。但是您对第二个“ if”有什么期望?真的要看 'DoSomething'可以改变a和b的(语义)相等吗?
语义相等的问题在于,它不能由编译器自动为对象生成,也不能从赋值中显而易见。必须为用户提供定义语义相等性的机制。在面向对象的语言中,该机制是一个继承的方法:equals。阅读一段OO代码,我们不希望某个方法在所有类中都具有完全相同的实现。我们习惯于继承和重载。
但是,对于运算符,我们期望相同的行为。当您看到'a == b'时,您应该在所有情况下都具有相同的相等类型(来自上面的4)。因此,为了保持一致性,语言设计人员对所有类型都使用了引用相等性。它不应该取决于程序员是否重写了方法。
PS:语言Dee与Java和C#略有不同:equals运算符表示简单类型的浅层相等,而用户定义的类的语义相等(实现=操作的责任在于用户-未提供默认值)。因为对于简单类型,浅相等始终是语义相等,所以语言是一致的。但是,它付出的代价是,对于用户定义的类型,默认情况下未定义equals运算符。您必须实现它。而且,有时候,这很无聊。