c#编译器要求每当自定义类型定义operator ==时,它也必须定义!=(参见这里)。

Why?

我很好奇为什么设计人员认为这是必要的,为什么编译器不能默认为一个合理的实现操作符时,只有另一个存在。例如,Lua只允许定义相等操作符,而免费获得另一个。c#也可以做到这一点,要求你定义==或同时定义==和!=,然后自动将缺少的!=运算符编译为!(左==右)。

我知道有一些奇怪的极端情况,一些实体可能既不相等也不相等(如IEEE-754 NaN),但这些似乎是例外,而不是规则。因此,这并不能解释为什么c#编译器设计人员将例外设置为规则。

我见过一些糟糕的情况,其中定义了相等运算符,然后不等式运算符是一个复制粘贴,每个比较都反转,每个&&切换到||(你明白了吧……(a==b)通过德摩根法则展开)。这是编译器可以通过设计消除的不良实践,就像Lua一样。

注意: 运算符< > <= >=也是如此。我无法想象在哪些情况下需要用非自然的方式来定义它们。Lua只允许您定义<和<=,并通过前两者的否定自然地定义>=和>。为什么c#不做同样的事情(至少在默认情况下)?

EDIT

显然,有充分的理由允许程序员根据自己的喜好执行相等和不相等的检查。一些答案指向了这样做可能不错的案例。

然而,我问题的核心是,为什么在c#中这是强制要求的,而通常这在逻辑上是不必要的?

It is also in striking contrast to design choices for .NET interfaces like Object.Equals, IEquatable.Equals IEqualityComparer.Equals where the lack of a NotEquals counterpart shows that the framework considers !Equals() objects as unequal and that's that. Furthermore, classes like Dictionary and methods like .Contains() depend exclusively on the aforementioned interfaces and do not use the operators directly even if they are defined. In fact, when ReSharper generates equality members, it defines both == and != in terms of Equals() and even then only if the user chooses to generate operators at all. The equality operators aren't needed by the framework to understand object equality.

基本上,. net框架并不关心这些操作符,它只关心一些Equals方法。要求用户同时定义==和!=操作符的决定纯粹与语言设计有关,而与. net关心的对象语义无关。


当前回答

你问题中的关键词是“为什么”和“必须”。

结果是:

这样回答是因为他们设计了这样,这是真的……但不回答你的"为什么"

回答说,有时独立地覆盖这两个可能是有帮助的,是真的…但没有回答你"必须"的问题。

我认为简单的答案是,c#没有任何令人信服的理由要求你重写这两者。

该语言应该只允许您重写==,并为您提供一个默认的!=实现,即!那如果你碰巧也想重写!=,那就试试吧。

这不是一个好的决定。人类设计语言,人类并不完美,c#也不完美。耸耸肩,回答问题

其他回答

如果你为你的自定义类型重载==,而不是!=,那么它将由object != object的!=操作符处理,因为所有东西都是从object派生的,这将与CustomType != CustomType有很大不同。

此外,语言的创造者可能希望这种方式为编码员提供最大的灵活性,也这样他们就不会对你打算做什么做假设。

如果你看一下。net源代码中==和!=重载的实现,它们通常不会实现!= as !(左==右)。他们用否定的逻辑完全实现它(比如==)。例如,DateTime实现了== as

return d1.InternalTicks == d2.InternalTicks;

And != as

return d1.InternalTicks != d2.InternalTicks;

如果你(或编译器,如果它隐式地做)要实现!= as

return !(d1==d2);

然后,您要对类引用的对象中==和!=的内部实现做一个假设。避免这种假设可能是他们决定背后的哲学。

可能只是一些他们没有想到也没有时间去做的事情。

我总是用你的方法当我超载==。然后我把它用在另一个。

你是对的,只需少量的工作,编译器就可以免费提供给我们。

嗯,这可能只是一个设计选择,但如你所说,x!= y不一定和!(x == y)相同。通过不添加默认实现,您可以确定不会忘记实现特定的实现。如果它确实像你说的那样微不足道,你可以用一个实现另一个。我不明白这怎么是“糟糕的练习”。

c#和Lua之间可能还有一些其他的区别…

要回答您的编辑,关于为什么如果您覆盖了一个,就必须覆盖两个,这都在继承中。

If you override ==, most likely to provide some sort of semantic or structural equality (for instance, DateTimes are equal if their InternalTicks properties are equal even through they may be different instances), then you are changing the default behavior of the operator from Object, which is the parent of all .NET objects. The == operator is, in C#, a method, whose base implementation Object.operator(==) performs a referential comparison. Object.operator(!=) is another, different method, which also performs a referential comparison.

In almost any other case of method overriding, it would be illogical to presume that overriding one method would also result in a behavioral change to an antonymic method. If you created a class with Increment() and Decrement() methods, and overrode Increment() in a child class, would you expect Decrement() to also be overridden with the opposite of your overridden behavior? The compiler can't be made smart enough to generate an inverse function for any implementation of an operator in all possible cases.

However, operators, though implemented very similarly to methods, conceptually work in pairs; == and !=, < and >, and <= and >=. It would be illogical in this case from the standpoint of a consumer to think that != worked any differently than ==. So, the compiler can't be made to assume that a!=b == !(a==b) in all cases, but it's generally expected that == and != should operate in a similar fashion, so the compiler forces you to implement in pairs, however you actually end up doing that. If, for your class, a!=b == !(a==b), then simply implement the != operator using !(==), but if that rule does not hold in all cases for your object (for instance, if comparison with a particular value, equal or unequal, is not valid), then you have to be smarter than the IDE.

真正的问题应该问是为什么<、>、< =和> =是对比较操作符必须同时实现,当在数值上! (b < b) = = > =和! (a > b) = = < = b。你应该需要实现所有四个如果你覆盖,你应该需要重写= =(和! =),因为(< = b) = = (a = = b)如果一个语义等于b。