C#编译器要求,每当自定义类型定义运算符==
,它还必须定义!=
(请参见此处 )。 程序员
为何? 框架
我很好奇,为何设计师会认为这是必要的,以及为何当仅存在另外一个运算符时,编译器为什么不能默认其中一个运算符为合理的实现。 例如,Lua容许您仅定义相等运算符,而另外一个则免费。 C#能够经过要求您定义==或同时定义==和!=来完成相同的工做,而后将缺乏的!=运算符自动编译为!(left == right)
。 测试
我知道有些状况下有些实体可能不相等或不相等(例如IEEE-754 NaN),可是在某些特殊状况下,这彷佛是个例外,而不是规则。 所以,这不能解释为何C#编译器设计人员将例外做为规则。 spa
我已经看到了定义平等运算符的工艺不佳的状况,而后不平等运算符是一个复制粘贴,每一个比较都相反,每一个&&切换为||。 (您明白了……基本上!(a == b)经过De Morgan的规则扩展了)。 与Lua的状况同样,编译器能够经过设计消除这种糟糕的作法。 .net
注意:运算符<> <=> =也是如此。 我没法想象须要用不天然的方式定义它们的状况。 Lua容许您仅定义<和<=,并经过前者的否认天然定义> =和>。 C#为何不作一样的事情(至少是“默认”)? 设计
编辑 code
显然,有充分的理由容许程序员执行他们喜欢的相等性和不平等性检查。 一些答案指出了可能不错的状况。 对象
可是,个人问题的核心是,为何在C#中一般在逻辑上没必要要时强制执行此操做? 接口
它与.NET接口(如Object.Equals
, IEquatable.Equals
IEqualityComparer.Equals
设计选择造成鲜明对比,在缺乏NotEquals
对应项的状况下,该框架认为!Equals()
对象是不相等的,仅此NotEquals
。 此外,诸如Dictionary
类的类和诸如.Contains()
类的方法.Contains()
取决于上述接口,即便定义了它们也不会直接使用运算符。 实际上,当ReSharper生成相等成员时,它会根据Equals()
定义==
和!=
,而且即便在用户选择彻底生成运算符的状况下也是如此。 框架不须要相等运算符来了解对象相等。 字符串
基本上,.NET框架不关心这些运算符,而仅关心一些Equals
方法。 要求用户同时定义==和!=运算符的决定彻底与语言设计有关,而就.NET而言,与对象语义无关。
可能只是他们没有想到的事情而没有时间去作。
当我重载==时,我老是使用您的方法。 而后,我只在另外一个中使用它。
您是对的,只需少许工做,编译器即可以避免费将其提供给咱们。
好吧,这可能只是设计选择,可是正如您所说, x!= y
没必要与!(x == y)
。 经过不添加默认实现,能够确保您不会忘记实现特定实现。 并且,若是确实如您所说的那样琐碎,则能够仅使用另外一个实现。 我看不出这是“不良作法”。
C#和Lua之间可能还有其余差别...
多是由于有人须要实现三值逻辑(即null
)。 在这种状况下-例如ANSI标准SQL-不能简单地根据输入否认运算符。
您可能遇到如下状况:
var a = SomeObject();
a == true
返回false
而a == false
也返回false
。
除了C#在许多方面适合C ++以外,我能想到的最好解释是,在某些状况下,您可能想采用稍微不一样的方法来证实“不平等”而不是证实“平等”。
显然,用字符串比较,例如,你能够只测试平等和return
循环了,当你看到非匹配的字符。 可是,它可能没有那么复杂的问题。 我想到了绽开过滤器 ; 快速判断元素是否不在集合中很是容易,可是很难判断元素是否在集合中。 尽管可使用相同的return
技术,但代码可能并不那么漂亮。
若是您在.net源代码中查看==和!=重载的实现,则它们一般不会将!=实现为!(左==右)。 他们使用否认逻辑彻底实现了它(例如==)。 例如,DateTime将==实现为
return d1.InternalTicks == d2.InternalTicks;
和!= as
return d1.InternalTicks != d2.InternalTicks;
若是您(或编译器(若是隐式执行))将实现=
return !(d1==d2);
那么您将在类引用中对==和!=的内部实现进行假设。 避免这种假设多是他们决定背后的哲学。