简体   繁体   中英

linq & distinct, implementing the equals & gethashcode

so I'm trying to make this work and I can't seem to know why it doesn't work

demo code;

namespace ConsoleApplication1
{
class Program
{
    static void Main(string[] args)
    {
        var myVar = new List<parent >();
        myVar.Add(new parent() { id = "id1", blah1 = "blah1", c1 = new child() { blah2 = "blah2", blah3 = "blah3" } });
        myVar.Add(new parent() { id = "id1", blah1 = "blah1", c1 = new child() { blah2 = "blah2", blah3 = "blah3" } });

        var test = myVar.Distinct();

        Console.ReadKey();

    }
}


public class parent : IEquatable<parent>
{
    public String id { get;set;}
    public String blah1 { get; set; }
    public child c1 { get; set; }

    public override int GetHashCode()
    {
        unchecked // Overflow is fine, just wrap
        {
            int hash = 17;
            // Suitable nullity checks etc, of course :)
            hash = hash * 23 + id.GetHashCode();
            hash = hash * 23 + blah1.GetHashCode();
            hash = hash * 23 + (c1 == null ? 0 : c1.GetHashCode());
            return hash;
        }
    }

    public bool Equals(parent other)
    {
        return object.Equals(id, other.id) &&
            object.Equals(blah1, other.blah1) &&
            object.Equals(c1, other.c1);
    }

}

public class child : IEquatable<child>
{
    public String blah2 { get; set; }
    public String blah3 { get; set; }

    public override int GetHashCode()
    {
        unchecked // Overflow is fine, just wrap
        {
            int hash = 17;
            // Suitable nullity checks etc, of course :)
            hash = hash * 23 + blah2.GetHashCode();
            hash = hash * 23 + blah3.GetHashCode();
            return hash;
        }
    }

    public bool Equals(child other)
    {
        return object.Equals(blah2, other.blah2) &&
            object.Equals(blah3, other.blah3);
    }

}
}

anyone could spot my error(s) ?

You need to override the Equals(object) method:

public override bool Equals(object obj) {
    return Equals(obj as parent);
}

The object.Equals method (unlike EqualityComparer<T>.Default ) does not use the IEquatable interface. Therefore, when you write object.Equals(c1, other.c1) , it doesn't call your Child.Equals(Child) method.

You don't absolutely need to do that for parent as well, but you really should.

Either you do what SLaks suggests, or you use EqualityComparer<child>.Default in your parent class to use your IEquatable<child> implementation:

  public bool Equals(parent other)
  {
   return object.Equals(id, other.id) &&
    object.Equals(blah1, other.blah1) &&
    EqualityComparer<child>.Default.Equals(c1, other.c1);
 }

When adding the calculating the hash you might want to try something like

hash ^= id.GetHashCode();

Not sure if that is what is causing your issue.

There are several things to get right here. If I'm going to implement any aspect of equality in a class such as GetHashCode , overriding == or IEquatable , I always use the following pattern.

  1. Override Equals
  2. Override GetHashCode
  3. Implement IEquatable<T> which means implementing Equals(T)
  4. Implement !=
  5. Implement ==

So, if I had a class named ExpiryMonth with properties Year and Month, this is how that implementation would look. It's a fairly mindless task now to adapt for other types of classes.

I have based this pattern on several other stackoverflow answers which all deserve credit, but which I haven't tracked along the way.

By always implementing all of these elements together, it ensures proper equality operations in a variety of contexts including dictionaries and Linq operations.

    public static bool operator !=(ExpiryMonth em1, ExpiryMonth em2)
    {
        if (((object)em1) == null || ((object)em2) == null)
        {
            return !Object.Equals(em1, em2);
        }
        else
        {
            return !(em1.Equals(em2));
        }
    }
    public static bool operator ==(ExpiryMonth em1, ExpiryMonth em2)
    {
        if (((object)em1) == null || ((object)em2) == null)
        {
            return Object.Equals(em1, em2);
        }
        else
        {
            return em1.Equals(em2);
        }
    }
    public bool Equals(ExpiryMonth other)
    {
        if (other == null) { return false; }
        return Year == other.Year && Month == other.Month;
    }
    public override bool Equals(object obj)
    {
        if (obj == null) { return false; }
        ExpiryMonth em = obj as ExpiryMonth;
        if (em == null) { return false; }
        else { return Equals(em); }
    }
    public override int GetHashCode()
    {
        unchecked // Overflow is not a problem
        {
            var result = 17;
            result = (result * 397) + Year.GetHashCode();
            result = (result * 397) + Month.GetHashCode();
            return result;
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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