Equals method implementation helpers (C#)

2020-06-16 03:36发布

问题:

Everytime I write some data class, I usually spend so much time writing the IEquatable implementation.

The last class I wrote was something like:

public class Polygon
{
    public Point[] Vertices { get; set; }
}

Implementing IEquatable was exaustive. Surely C#3.0/LINQ helps a lot, but the vertices can be shifted and/or in the reverse order, and that adds a lot of complexity to the Equals method. After many unit tests, and corresponding implementation, I gave up, and changed my application to accept only triangles, which IEquatable implementation required only 11 unit tests to be fully covered.

There is any tool or technique that helps implementing Equals and GetHashCode?

回答1:

I use ReSharper to generate equality members. It will optionally implement IEquatable<T> as well as overriding operators if you want that (which of course you never do, but it's cool anyway).

The implementation of Equals includes an override of Object.Equals(Object), as well as a strongly typed variant (which can avoid unnecessary type checking). The lesser typed version calls the strongly typed one after performing a type check. The strongly typed version performs a reference equality check (Object.ReferenceEquals(Object,Object)) and then compares the values of all fields (well, only those that you tell the generator to include).

As for GetHashCode, a smart factorisation of the field's GetHashCode values are combined (using unchecked to avoid overflow exceptions if you use the compiler's checked option). Each of the field's values (apart from the first one) are multiplied by prime numbers before being combined. You can also specify which fields would never be null, and it'll drop any null checks.

Here's what you get for your Polygon class by pressing ALT+Insert then selecting "Generate Equality Members":

public class Polygon : IEquatable<Polygon>
{
    public Point[] Vertices { get; set; }

    public bool Equals(Polygon other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Equals(other.Vertices, Vertices);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != typeof (Polygon)) return false;
        return Equals((Polygon) obj);
    }

    public override int GetHashCode()
    {
        return (Vertices != null ? Vertices.GetHashCode() : 0);
    }
}

Some of the features I talked about above don't apply as there is only one field. Note too that it hasn't checked the contents of the array.

In general though, ReSharper pumps out a lot of excellent code in just a matter of seconds. And that feature is pretty low on my list of things that makes ReSharper such an amazing tool.



回答2:

For comparing two arrays of items, I use the SequenceEqual extension method.

As for a generic Equals and GetHashCode, there's a technique based on serialization that might work for you.

Using MemoryStream and BinaryFormatter for reuseable GetHashCode and DeepCopy functions