我遇到了一些问题,所以我想问一下是否有人对这是否是一种为自定义不可变类实现Equals方法和等式/不等式运算符的有效方法有任何反馈。我的程序经常调用这些操作符,所以我想确保我把它们弄好。
class MyObj
{
public static bool operator ==(MyObj a, MyObj b)
{
if (!object.ReferenceEquals(a, null))
return a.Equals(b);
else if (!object.ReferenceEquals(b, null))
return b.Equals(a);
else
// both are null
return true;
}
public static bool operator !=(MyObj a, MyObj b)
{
if (!object.ReferenceEquals(a, null))
return !a.Equals(b);
else if (!object.ReferenceEquals(b, null))
return !b.Equals(a);
else
// both are null
return false
}
public override bool Equals(object obj)
{
return this.Equals(obj as MyObj);
}
public bool Equals(MyObj obj)
{
if (object.ReferenceEquals(obj, null))
return false;
else
return (obj.FieldOne == this.FieldOne &&
obj.FieldTwo == this.FieldTwo && ...);
}
}
答案 0 :(得分:2)
我使用以下代码片段作为参考类型,在我看来,它具有较少的重复和感觉更清晰。使用静态“Equals”方法允许.NET语言在没有运算符重载的情况下比较您的实例,而无需在调用实例方法之前测试null。如果你正在实现相等运算符,那么最好让你的类不可变,如果可以的话。
class Foo : IEquatable<Foo>
{
public override bool Equals(object obj)
{
return Equals(obj as Foo);
}
public bool Equals(Foo other)
{
if (object.ReferenceEquals(other, null)) return false;
// Optional early out
if (object.ReferenceEquals(this, other)) return true;
// Compare fields here
}
public static bool Equals(Foo a, Foo b)
{
if (ReferenceEquals(a, null)) return ReferenceEquals(b, null);
return a.Equals(b);
}
public static bool operator ==(Foo a, Foo b)
{
return Equals(a, b);
}
public static bool operator !=(Foo a, Foo b)
{
return !Equals(a, b);
}
}
答案 1 :(得分:2)
我注意到的一些事情:
Equals
,因此您还应覆盖GetHashCode
。Equals(MyObj)
方法是整个IEquatable<MyObj>
接口的有效实现,MyObj
确实应该实现该接口。这也允许Dictionary<>
等直接利用您的Equals(MyObj)
方法,而不是通过Equals(object)
。此外,我完全赞同Trillian的替代实现,除了我将[{1}}直接实现为a != b
而不是!(a == b)
。 (当然是微不足道的。)
答案 2 :(得分:0)
基本上是的,但是要纠正错误。
Equals(object)
方法调用自身而不是调用Equals(MyObj)
方法,从而导致永久循环。它应该是:
public override bool Equals(object obj) {
MyObj other = obj as MyObj;
return this.Equals(other);
}
或简单地说:
public override bool Equals(object obj) {
return this.Equals(obj as MyObj);
}
此外,您可以将不等式运算符简化为:
public static bool operator !=(MyObj a, MyObj b) {
return !(a == b);
}
答案 3 :(得分:0)
如果您正在寻找效率,我建议您使用此代替object.ReferenceEquals(foo, null)
:
(object)foo == null
这实际上是等效的,但避免了函数调用。
我还希望在覆盖IEquatable<T>
的所有类型中实施Equals
。对于参考类型,我然后将Equals(object)
转发给Equals(Foo)
。
public override bool Equals(object other){return Equals(other as Foo);}
操作符重载可以简化为:
public static bool operator==(Foo a, Foo b){
if((object)a == null)
return (object)b == null;
return a.Equals(b);
}
public static bool operator!=(Foo a, Foo b){
return !(a == b);
}
如果需要绝对效率,可能值得在这些函数中重复一些代码以避免额外的函数调用,但与使用(object)foo == null
而不是{{1避免函数调用需要额外的代码来维护,因此小的增益可能不值得。
答案 4 :(得分:0)
我更愿意将所有“如果这是空的,那就做其他......”逻辑留给框架:
class MyObj : IEquatable<MyObj> {
public static bool operator ==( MyObj left, MyObj right ) {
return EqualityComparer<MyObj>.Default.Equals( left, right );
}
public static bool operator !=( MyObj left, MyObj right ) {
return !EqualityComparer<MyObj>.Default.Equals( left, right );
}
public override bool Equals( object obj ) {
return this.Equals( obj as MyObj );
}
public bool Equals( MyObj other ) {
return !object.ReferenceEquals( other, null )
&& obj.FieldOne == this.FieldOne
&& obj.FieldTwo == this.FieldTwo
&& ...
;
}
...
}
另请参阅What is the best algorithm for an overridden GetHashCode?了解GetHashCode
。