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