Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
407 views
in Technique[技术] by (71.8m points)

c# - Differences between IEquatable<T>, IEqualityComparer<T>, and overriding .Equals() when using LINQ on a custom object collection?

I'm having some difficulty using Linq's .Except() method when comparing two collections of a custom object.

I've derived my class from Object and implemented overrides for Equals(), GetHashCode(), and the operators == and !=. I've also created a CompareTo() method.

In my two collections, as a debugging experiment, I took the first item from each list (which is a duplicate) and compared them as follows:

itemListA[0].Equals(itemListB[0]);     // true
itemListA[0] == itemListB[0];          // true
itemListA[0].CompareTo(itemListB[0]);  // 0

In all three cases, the result is as I wanted. However, when I use Linq's Except() method, the duplicate items are not removed:

List<myObject> newList = itemListA.Except(itemListB).ToList();

Learning about how Linq does comparisons, I've discovered various (conflicting?) methods that say I need to inherit from IEquatable<T> or IEqualityComparer<T> etc.

I'm confused because when I inherit from, for example, IEquatable<T>, I am required to provide a new Equals() method with a different signature from what I've already overridden. Do I need to have two such methods with different signatures, or should I no longer derive my class from Object?

My object definition (simplified) looks like this:

public class MyObject : Object
{
    public string Name {get; set;}
    public DateTime LastUpdate {get; set;}

    public int CompareTo(MyObject other)
    {
        // ...
    }

    public override bool Equals(object obj)
    {
        // allows some tolerance on LastUpdate
    }

    public override int GetHashCode()
    {
        unchecked
        {
            int hash = 17;
            hash = hash * 23 + Name.GetHashCode();
            hash = hash * 23 + LastUpdate.GetHashCode();
            return hash;
        }
    }

    // Overrides for operators
}

I noticed that when I inherit from IEquatable<T> I can do so using IEquatable<MyObject> or IEquatable<object>; the requirements for the Equals() signature change when I use one or the other. What is the recommended way?

What I am trying to accomplish:

I want to be able to use Linq (Distinct/Except) as well as the standard equality operators (== and !=) without duplicating code. The comparison should allow two objects to be considered equal if their name is identical and the LastUpdate property is within a number of seconds (user-specified) tolerance.

Edit:

Showing GetHashCode() code.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

It doesn't matter whether you override object.Equals and object.GetHashCode, implement IEquatable, or provide an IEqualityComparer. All of them can work, just in slightly different ways.

1) Overriding Equals and GetHashCode from object:

This is the base case, in a sense. It will generally work, assuming you're in a position to edit the type to ensure that the implementation of the two methods are as desired. There's nothing wrong with doing just this in many cases.

2) Implementing IEquatable

The key point here is that you can (and should) implement IEquatable<YourTypeHere>. The key difference between this and #1 is that you have strong typing for the Equals method, rather than just having it use object. This is both better for convenience to the programmer (added type safety) and also means that any value types won't be boxed, so this can improve performance for custom structs. If you do this you should pretty much always do it in addition to #1, not instead of. Having the Equals method here differ in functionality from object.Equals would be...bad. Don't do that.

3) Implementing IEqualityComparer

This is entirely different from the first two. The idea here is that the object isn't getting it's own hash code, or seeing if it's equal to something else. The point of this approach is that an object doesn't know how to properly get it's hash or see if it's equal to something else. Perhaps it's because you don't control the code of the type (i.e. a 3rd party library) and they didn't bother to override the behavior, or perhaps they did override it but you just want your own unique definition of "equality" in this particular context.

In this case you create an entirely separate "comparer" object that takes in two different objects and informs you of whether they are equal or not, or what the hash code of one object is. When using this solution it doesn't matter what the Equals or GetHashCode methods do in the type itself, you won't use it.


Note that all of this is entirely unrelated from the == operator, which is its own beast.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...