Search code examples
c#collectionsoperatorsequalsequality

C# - equality operator with list of interface type not working as expected


Consider the following code, where ClassOne is a class derived from IClass:

List<IClass> list = new List<IClass>();
list.Add(new ClassOne("foo", "bar"));
list.Add(new ClassOne("baz", "bam"));

List<IClass> list2 = new List<IClass>();
list2.Add(new ClassOne("foo", "bar"));
list2.Add(new ClassOne("baz", "bam"));

if (list == list2)
    Console.WriteLine("Lists are equal.");
else
    Console.WriteLine("Lists are NOT equal.");

The equality operator returns false (i.e. lists do not match), besides the fact the the operator ==, operator !=, Equals(ClassOne), Equals(object) and GetHashCode() have been implemented/overridden for ClassOne. Why is that? I would expect the equality operator to return true. Are there any other methods/interfaces that must be implemented for the == operator to work as expected?

For reference, here's the implementation of ClassOne and IClass:

public interface IClass
{
    string getA();
    string getB();
} //interface


public class ClassOne : IClass, IEquatable<ClassOne>
{
    public ClassOne(string a, string b)
    {
        strA = a;
        strB = b;
    }

    public string getA()
    {
        return strA;
    }

    public string getB()
    {
        return strB;
    }

    public bool Equals(ClassOne other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        if (!string.Equals(strA, other.strA))
            return false;
        return string.Equals(strB, other.strB);
    }

    public override bool Equals(object other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        if (other is ClassOne)
        {
            ClassOne c1 = (ClassOne)other;
            return Equals(c1);
        }
        //not ClassOne, so it is not equal
        return false;
    }

    public override int GetHashCode()
    {
        int hc_a = -1;
        if (null != strA)
            hc_a = strA.GetHashCode();
        int hc_b = -1;
        if (null != strB)
            hc_b = strB.GetHashCode();
        return hc_a ^ hc_b;
    }

    public static bool operator ==(ClassOne left, ClassOne right)
    {
        if (ReferenceEquals(left, right)) return true;
        if (ReferenceEquals(left, null) || ReferenceEquals(right, null))
            return false;
        return left.Equals(right);
    }

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

    private string strA, strB;
} //class

Any help or hints in the right direction will be appreciated. Thanks.


Solution

  • Why is that? I would expect the equality operator to return true.

    Not correct - the == operator (and Equals()) are not defined on List<T> to check the equality of its contents - it defaults to reference equality from the operator defined on object. Since the two lists are different objects, == returns false.

    You can use the Linq SequenceEqual method to determine if two lists contain equal objects in the same order:

    if (list.SequenceEqual(list2))
        Console.WriteLine("Lists are equal.");
    else
        Console.WriteLine("Lists are NOT equal.");