views:

284

answers:

4

So, the following lambda expression is not returning any elements in the collection, even though while stepping through I was able to verify that 1 item matches the criteria. I've added a sample of the class with it's IEquatable implementation.

...within a method, foo is a method parameter
var singleFoo = _barCollection.SingleOrDefault(b => b.Foo == foo);

The above returns nothing. Any suggestions as to what to do to make the expression above work?

public class Foo: IEquatable<Foo> 
{
    public string KeyProperty {get;set;}
    public bool Equals(Foo other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return other.KeyProperty==KeyProperty;
    }
    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != typeof (Foo)) return false;
        return Equals((Foo) obj);
    }
    public override int GetHashCode()
    {
        return (KeyProperty != null ? KeyProperty.GetHashCode() : 0);
    }
}

To make sure I didn't go insane, I created the following nUnit test which passes:

    [Test]
    public void verify_foo_comparison_works()
    {
        var keyString = "keyValue";
        var bar = new Bar();
        bar.Foo = new Foo { KeyProperty = keyString };
        var basicFoo = new Foo { KeyProperty = keyString };
        var fromCollectionFoo = Bars.SingleFooWithKeyValue;
        Assert.AreEqual(bar.Foo,basicFoo);
        Assert.AreEqual(bar.Foo, fromCollectionFoo);
        Assert.AreEqual(basicFoo, fromCollectionFoo);
    }

Attempt at overriding == and !=:

    public static bool operator ==(Foo x, Foo y)
    {
        if (ReferenceEquals(x, y))
            return true;
        if ((object)x == null || (object)y == null)
            return false;
        return x.KeyProperty == y.KeyProperty;
    }
    public static bool operator !=(Foo x, Foo y)
    {
        return !(x == y);
    }
+3  A: 

They use EqualityComparer<T>.Default for equality comparisons and Comparer<T>.Default for ordered comparisons.

MSDN - EqualityComparer<T>.Default Remarks:

The Default property checks whether type T implements the System.IEquatable(Of T) generic interface and if so returns an EqualityComparer(Of T) that uses that implementation. Otherwise it returns an EqualityComparer(Of T) that uses the overrides of Object.Equals and Object.GetHashCode provided by T.

MSDN - Comparer<T>.Default Remarks:

The Comparer(Of T) returned by this property uses the System.IComparable(Of T) generic interface (IComparable<T> in C#, IComparable(Of T) in Visual Basic) to compare two objects. If type T does not implement the System.IComparable(Of T) generic interface, this property returns a Comparer(Of T) that uses the System.IComparable interface.

Mehrdad Afshari
A: 

You need to also override the == operator.

David Morton
I'd rather switch to using the`Equals` method, like @Timores does.
Svish
+3  A: 

You are not redefining the == operator.

Change to:

var singleFoo = _barCollection.SingleOrDefault(b => b.Foo.Equals(foo));
Timores
Thanks for your suggestion. However, I'd rather not refactor all the places where the "bar.Foo == foo" has been implemented.
Carlos Bittencourt
No problem. Define == on Foo and you can keep the predicates as is.
Timores
Overwrote == and != as below and still no such luck. I gave up and refactored to use your initial suggestion. (I updated the original post to show the == and != implementations.)
Carlos Bittencourt
A: 

I've hit a similar problem with an IdentityKey class (that has implicit Guid conversions) - in this instance it's not really feasible to include an == operator override.

I say it's not feasible because with "mixed" comparisons I'll now have to cast the types to avoid ambiguity for the == operator overloads. ie. Instead of if(guidValue = identityKeyValue) it has to become if((IdentityKey)guidValue == identityKeyValue)

Whilst using .Equals is a solution, it doesn't feel natural to me?

Rich
If this is intended to be a question, please post it as a *question* and not as an *answer*. If it in some way depends on this question, link it in your own question and explain why it does not solve your problem.
GalacticCowboy