C# equality checking

拈花ヽ惹草 提交于 2019-12-28 12:06:00

问题


What's your approach on writing equality checks for the structs and classes you create?

1) Does the "full" equality checking require that much of boilerplate code (like override Equals, override GetHashCode, generic Equals, operator==, operator!=)?

2) Do you specify explicitly that your classes model the IEquatable<T> interface?

3) Do I understand correctly, that there is no actual way to automatically apply Equals overrides, when I invoke something like a == b and I always have to implement both the Equals and operator== members?


回答1:


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



回答2:


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.




回答3:


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. :)




回答4:


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..



来源:https://stackoverflow.com/questions/4420901/c-sharp-equality-checking

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