Pergunta

Is it possible to perform union/except on Lists of Objects where the instance of objects are not necessarily the same but they are functionally equivalent?

What I mean is if I have a class like this,

Class A
{
    String a;
    int b;
    double c;
}

And I had the following Lists:

A foo = new A() {"a",2,3.4}    
A bar = new A() {"a",2,3.4}

List<A> firstList = new List<A>() { foo }
List<A> secondList = new List<A>() { bar }

How can I perform firstList.Except/Union on secondList if firstList and secondList had completely different object instances but the fields/properties of the objects are exactly the same?

Foi útil?

Solução

You need to overload the Equals method of your class.

Right now, the way that it checks for equality is by checking the reference. There's a way to fix that, by overriding the Equals method:

class A
{
    string a;
    int b;
    double c;
    public override bool Equals(object obj)
    {
        A aobj = obj as A;
        if (aobj == null) return false;
        return a == aobj.a && b == aobj.b && c == aobj.c;
    }
}

However, for these functions to perform at their best, you also need to override the GetHashCode method, too. Like this:

class A
{
    string a;
    int b;
    double c;
    public override bool Equals(object obj)
    {
        return a == obj.a && b == obj.b && c == obj.c;
    }
    public override int GetHashCode()
    {
        unchecked { return 17 * (a ?? "").GetHashCode() * b * c.GetHashCode(); }
    }
}

Outras dicas

Simply override the object.Equals method to tell the world when to treat your objects as equal. Your class A should look something like this:

class A
{
    string a;
    int b;
    double c;

    public override bool Equals(object obj)
    {
        if (!(obj is A)) return obj.Equals(this); // defer to other object
        A other = (A)obj;
        return a == other.a && b == other.b && c == other.c; // check field equality
    }
    public override int GetHashCode()
    {
        int hc = 13;
        hc += a.GetHashCode() * 27;
        hc += b.GetHashCode() * 27;
        hc += c.GetHashCode() * 27;
    }
}

Adding little more to previous answers. Overriding Equals will require overriding == and !=

public class A
{
    String a;
    int b;
    double c;

    public override bool Equals(object obj)
    {   
        if (object.ReferenceEquals(null, obj))
        {
            return false;
        }

        if (object.ReferenceEquals(this, obj))
        {
            return true;
        }

        if (obj.GetType() != typeof(A))
        {
            return false;
        }

        var other = obj as A;
        return string.Equals(this.a, other.a) && this.b == other.b && this.c == other.b;
    }

    public override int GetHashCode()
    {
        if (string.IsNullOrEmpty(a))
        {
            return this.b.GetHashCode() ^ this.c.GetHashCode();
        }
        return this.a.GetHashCode() ^ this.b.GetHashCode() ^ this.c.GetHashCode();
    }

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

        if (object.ReferenceEquals(null, left))
        {
            return false;
        }

        if (object.ReferenceEquals(null, right))
        {
            return false;
        }

        return left.Equals(right);
    }

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

You can use LINQ to Objects to create intersections and unions on lists - and there is no need to override Equals and GetHashCode. Use the Enumerable.Intersect and Enumerable.Except methods:

public class A
{
    public String a;
    public int b;
    public double c;
}
A foo = new A() {a = "a", b = 2, c = 3.4};
A bar = new A() {a = "a", b = 2, c = 3.4};

List<A> firstList = new List<A>() { foo };
List<A> secondList = new List<A>() { bar };

firstList.Except(secondList);
firstList.Intersect(secondList);

The output in this case is:

same entries: 
>> IEnumerable<A> (1 item) 4  
>> a b c 
>> a 2 3,4 

You can make combinations firstList.Method(secondsList) and vice versa. You could event write a custom Comparer - IEqualityComparer in order to compare complex object types.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top