Question

I am using a System.Collections.Generic, which contains instances of a class I wrote.

I have read that the collections .Contains method uses object.Equals(), or an implementation of the Equals() method from the IEquatable interface.

I have overridden the object method, as well as implemented from the interface. However, Queue.Contains(instance) is always returning false. What am I doing wrong?

For instance...

class Foo : IEquatable<Foo>
{
    ...
    int fooField1;
    ...

    public override bool Equals(object obj)
    {
         Foo other = obj as Foo;
         bool isEqual = false;

         if (other.fooField1 == this.fooField1)
         { 
             isEqual = true;
         }

         return isEqual;         
    }

    public bool Equals(Foo other)
    {
         bool isEqual = false;

         if (other.fooField1 == this.fooField1)
         { 
             isEqual = true;
         }

         return isEqual;         
    }

}
...

void SomeFunction()
{
    Queue<Foo> Q = new Queue<Foo>();
    Foo fooInstance1 = new Foo();
    Foo fooInstance2 = new Foo();

    fooInstance1.fooField1 = 5;
    fooInstance2.fooField1 = 5;

    Q.Enqueue(fooInstanec1);
    if(Q.Contains(fooInstance2) == false)
    {
         Q.Enqueue(fooInstance2);
    }
}

fooInstance2 is always added to the queue. In fact, when I run this on the debugger, the implementations of Equals are never reached.

What am I doing wrong?

Was it helpful?

Solution

Your sample code works as expected once the initial compile errors are sorted out. Not that it is relevant to the posed problem, Do read up on overriding Equals (you need to override GetHashCode too and check for error cases like null / type-mismatch).

class Foo : IEquatable<Foo>
    {
        private int _fooField1;
        public Foo(int value)
        {
            _fooField1 = value;
        }

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

        public bool Equals(Foo other)
        {
            return (other._fooField1 == this._fooField1);
        }
    }



    class Program
    {
        static void SomeFunction()
        {
            var Q = new Queue<Foo>();
            Foo fooInstance1 = new Foo(5);
            Foo fooInstance2 = new Foo(5);

            Q.Enqueue(fooInstance1);
            if (!Q.Contains(fooInstance2))
            {
                Q.Enqueue(fooInstance2);
            }
            else
            {
                Console.Out.WriteLine("Q already contains an equivalent instance ");
            }
        }

        static void Main(string[] args)
        {
            SomeFunction();
        }
    }

OTHER TIPS

You need GetHashCode() method overridden as well in your class Foo.

fooInstance1.fooField1 = 5;
fooInstance1.fooField2 = 5;

You updated fooInstance1 twice there. Second line should say fooInstance2.fooField1 = 5;.

Once that's fixed, Q.Contains returns True as expected.


Other than that, you don't necessarily need to implement IEquatable. Every object has an overridable Equals method. You can simply overwrite that. Be careful when you implement your own comparison method. Your implementation shown in this sample is very open to NullReference exceptions. Something like this would be better:

public override bool Equals(object obj)
{
    if(obj == null)
        return false;

    Foo other = obj as Foo;
    if(other == null)
        return false;

    return fooField1 == other.fooField1;
}

As others have mentioned, if you go this route and override Equals, you should override GetHashCode, too. There are a few other things you should consider. See this MSDN page for details.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top