C# equality checking

寵の児 提交于 2019-11-28 07:02:11

You're right, this is a lot of boiler-plate code, and you need to implement everything separately.

I would recommend:

  • If you're going to implement value equality at all, override GetHashCode and Equals(object) - creating overloads for == and implementing IEquatable<T> without doing that could result in very unexpected behaviour
  • I would always implement IEquatable<T> if you're overriding Equals(object) and GetHashCode
  • I only overload the == operator more rarely
  • Implementing equality correctly for unsealed classes is tricky, and can still produce surprising/undesirable results. If you need equality for types in a hierarchy, implement IEqualityComparer<T> expressing the comparison you're interested in.
  • Equality for mutable types is usually a bad idea, as two objects can be equal and then unequal later... if an object is mutated (in an equality-affecting way) after it's been used as a key in a hash table, you won't be able to find it again.
  • Some of the boiler-plate is slightly different for structs... but like Marc, I very rarely write my own structs.

Here's a sample implementation:

using System;

public sealed class Foo : IEquatable<Foo>
{
    private readonly string name;
    public string Name { get { return name; } }

    private readonly int value;
    public int Value { get { return value; } }

    public Foo(string name, int value)
    {
        this.name = name;
        this.value = value;
    }

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

    public override int GetHashCode()
    {
        int hash = 17;
        hash = hash * 31 + (name == null ? 0 : name.GetHashCode());
        hash = hash * 31 + value;
        return hash;
    }

    public bool Equals(Foo other)
    {
        if ((object) other == null)
        {
            return false;
        }
        return name == other.name && value == other.value;
    }

    public static bool operator ==(Foo left, Foo right)
    {
        return object.Equals(left, right);
    }

    public static bool operator !=(Foo left, Foo right)
    {
        return !(left == right);
    }
}

And yes, that's a heck of a lot of boilerplate, very little of which changes between implementations :(

The implementation of == is slightly less efficient than it might be, as it will call through to Equals(object) which needs to do the dynamic type check... but the alternative is even more boiler-plate, like this:

public static bool operator ==(Foo left, Foo right)
{
    if ((object) left == (object) right)
    {
        return true;
    }

    // "right" being null is covered in left.Equals(right)
    if ((object) left == null)
    {
        return false;
    }
    return left.Equals(right);
}

I rarely do anything special for classes; for most regular objects referential equality works fine.

I even more rarely write a struct; but since structs represent values it is usually appropriate to provide equality etc. This would usually involve everything; Equals, ==, != and IEquatable<T> (since this avoids boxing in scenarios using EqualityComparer<T>.Default.

The boilerplate isn't usually too problematic, but IIRC tools like resharper can help here.

Yes, it is advisable to keep Equals and == in sync, and this needs to be done explicitely.

You just need to implement operator== for a==b .
As I like my data in dictionaries sometimes I override GetHashCode.
Next I implement Equals (as an unmentioned standard ... this is because there is no constraint for equality when using generics) and specify implementing IEquatable. Since I am going to do this I might as well point my == and != implementations to Equals. :)

nawfal

See What is "Best Practice" For Comparing Two Instances of a Reference Type?

You can avoid boiler plate code (hope C#/VS team brings something easy for developers in their next iteration) with the help of a snippet, here is one such..

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!