简体   繁体   English

这是实现Equals和平等/不平等运算符的好/有效的习惯用法吗?

[英]Is this a good/efficient idiom for implementing Equals and equality/inequality operators?

I have had a few problems getting this right, so I wanted to ask if anyone has any feedback on whether this is an efficient way to implement the Equals method and equality/inequality operators for a custom immutable class. 我有一些问题要做到这一点,所以我想问一下是否有人对这是否是一种有效的方法实现Equals方法和自定义不可变类的相等/不等运算符有任何反馈。 These operators are called very frequently by my program, so I want to make sure I get them right. 我的程序经常调用这些运算符,所以我想确保我把它们弄好。

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 && ...);
    }

}

I use the following code snippet for reference types, which has less duplication and feels cleaner, in my opinion. 我使用以下代码片段作为参考类型,在我看来,它具有较少的重复和感觉更清晰。 Having a static "Equals" method allows .NET languages without operator overloading to compare your instances without having to test for null before calling the instance method. 使用静态“Equals”方法允许.NET语言在没有运算符重载的情况下比较您的实例,而无需在调用实例方法之前测试null。 If you're implementing equality operators, it might also be best to make your class immutable, if you can. 如果您正在实现相等运算符,那么最好使您的类不可变,如果可以的话。

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);
    }
}

Some things I'm noticing: 我注意到的一些事情:

  • Because you're overriding Equals , you should also override GetHashCode . 因为你要覆盖Equals ,所以你也应该覆盖GetHashCode
  • Since your Equals(MyObj) method is a valid implementation for the entire IEquatable<MyObj> interface, MyObj should indeed implement that interface. 由于您的Equals(MyObj)方法是整个IEquatable<MyObj>接口的有效实现,因此MyObj确实应该实现该接口。 This will also allow Dictionary<> and such to directly take advantage of your Equals(MyObj) method, instead of going through Equals(object) . 这也将允许Dictionary<>等直接利用你的Equals(MyObj)方法,而不是通过Equals(object)

Also I completely agree with Trillian's alternative implementation, except I would've implemented a != b directly as !(a == b) instead of !Equals(a, b) . 此外,我完全赞同Trillian的替代实现,除了我将直接实现a != b !(a == b)而不是!Equals(a, b) (Trivial difference of course.) (当然是微不足道的。)

Basically yes, but there is an error to correct. 基本上是的,但有一个错误要纠正。

The Equals(object) method calls itself instead of calling the Equals(MyObj) method, causing an eternal loop. Equals(object)方法调用自身而不是调用Equals(MyObj)方法,从而导致永久循环。 It should be: 它应该是:

public override bool Equals(object obj) {
   MyObj other = obj as MyObj;
   return this.Equals(other);
}

or simply: 或者干脆:

public override bool Equals(object obj) {
   return this.Equals(obj as MyObj);
}

Also, you can simplify the inequality operator to: 此外,您可以将不等式运算符简化为:

public static bool operator !=(MyObj a, MyObj b) {
   return !(a == b);
}

If you're looking for efficiency, I recommend using this instead of object.ReferenceEquals(foo, null) : 如果您正在寻找效率,我建议使用它而不是object.ReferenceEquals(foo, null)

(object)foo == null

This is effectively equivalent but avoids a function call. 这实际上是等效的,但避免了函数调用。

I also like to implement IEquatable<T> in all my types that override Equals . 我也喜欢在我的所有类型中实现IEquatable<T>来覆盖Equals For reference types, I then forward Equals(object) to Equals(Foo) . 对于引用类型,我然后将Equals(object)转发给Equals(Foo)

public override bool Equals(object other){return Equals(other as Foo);}

The operator overloads can be simplified as so: 操作符重载可以简化为:

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);
}

If absolute efficiency is needed, though, it may be worth a little duplication of code in these functions to avoid the extra function calls, but unlike using (object)foo == null instead of object.ReferenceEquals(foo, null) , avoiding the function call requires extra code to maintain, so the small gain may not be worth it. 但是,如果需要绝对效率,可能值得在这些函数中重复一些代码以避免额外的函数调用,但与使用(object)foo == null而不是object.ReferenceEquals(foo, null) ,避免使用函数调用需要额外的代码来维护,因此小的增益可能不值得。

I prefer to leave all the "if this is null then do that else..." logic to the framework: 我更愿意将所有“如果这是空的,那么做其他......”逻辑留给框架:

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
        && ...
        ;
  }

  ...

}

See also What is the best algorithm for an overridden GetHashCode? 另请参阅重写的GetHashCode的最佳算法是什么? for implementing GetHashCode . 用于实现GetHashCode

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM