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

发布于 2024-08-10 10:13:39 字数 1103 浏览 8 评论 0原文

我在正确执行此操作时遇到了一些问题,因此我想询问是否有人对这是否是为自定义不可变类实现 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 && ...);
    }

}

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

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

野心澎湃 2024-08-17 10:13:40

我注意到的一些事情:

  • 因为您要重写 Equals,所以您还应该重写 GetHashCode
  • 由于您的 Equals(MyObj) 方法是整个 IEquatable 接口的有效实现,因此 MyObj 确实应该实现该接口。这也将允许 Dictionary<> 等直接利用您的 Equals(MyObj) 方法,而不是通过 Equals(object) >。

另外,我完全同意 Trillian 的替代实现,除了我会直接将 a != b 实现为 !(a == b) 而不是 !Equals( a,b)。 (当然差异很小。)

Some things I'm noticing:

  • Because you're overriding Equals, you should also override GetHashCode.
  • Since your Equals(MyObj) method is a valid implementation for the entire IEquatable<MyObj> interface, MyObj should indeed implement that interface. This will also allow Dictionary<> and such to directly take advantage of your Equals(MyObj) method, instead of going through 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). (Trivial difference of course.)

慕巷 2024-08-17 10:13:40

基本上是的,但是有一个错误需要纠正。

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

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. 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);
}
心房的律动 2024-08-17 10:13:40

如果您正在寻求效率,我建议使用它而不是 object.ReferenceEquals(foo, null)

(object)foo == null

这实际上是等效的,但避免了函数调用。

我还喜欢在所有覆盖 Equals 的类型中实现 IEquatable。对于引用类型,我然后将 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 而不是 object.ReferenceEquals(foo, null),避免函数调用需要额外的代码来维护,因此小小的收益可能不值得。

If you're looking for efficiency, I recommend using this instead of 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. For reference types, I then forward Equals(object) to 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.

梦萦几度 2024-08-17 10:13:40

我更喜欢将所有“如果这是 null 则执行其他操作...”逻辑留给框架:

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

  ...

}

另请参阅 重写 GetHashCode 的最佳算法是什么? 用于实现 GetHashCode

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? for implementing GetHashCode.

独自唱情﹋歌 2024-08-17 10:13:39

我将以下代码片段用于引用类型,在我看来,它的重复更少并且感觉更干净。拥有静态“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);
    }
}

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. 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);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文