1
using System;
using System.Collections.Generic;
using System.Linq;
using NUnit.Framework;

namespace UnitTest.Model
{
[TestFixture]
public class SampleEquatableObjectTest
{
    [Test]
    public void TwoIdenticalUsersComparedEqualTrue()
    {
        var user1 = new SampleObject { Id = 1, Name = "Test User" };
        var user2 = new SampleObject { Id = 1, Name = "Test User" };

        Assert.IsTrue(user1.Equals(user2));
    }

    [Test]
    public void TwoDifferentUsersComparedEqualFalse()
    {
        var user1 = new SampleObject { Id = 1, Name = "Test User 1" };
        var user2 = new SampleObject { Id = 2, Name = "Test User 2" };

        Assert.IsFalse(user1.Equals(user2));
    }

    [Test]
    public void CollectionOfUsersReturnsDistinctList()
    {
        var userList = new List<SampleObject>
                           {
                               new SampleObject {Id = 1, Name = "Test User"},
                               new SampleObject {Id = 1, Name = "Test User 1"},
                               new SampleObject {Id = 2, Name = "Test User 2"}
                           };

        Assert.AreEqual(userList.Count, 3);

        var result = userList.Distinct();

        Assert.AreEqual(result.Count(), 2);

        var multipleTest = (from r in result group r by new { r.Id } into multGroup where multGroup.Count() > 1 select multGroup.Key).Any();

        Assert.IsFalse(multipleTest);
    }

    public class SampleObject : IEquatable<SampleObject>
    {
        public int Id { get; set; }
        public string Name { get; set; }

        public bool Equals(SampleObject other)
        {
            if (ReferenceEquals(this, other))
                return true;
            if (ReferenceEquals(other, null) || ReferenceEquals(this, null))
                return false;

            return Id.Equals(other.Id);
        }
    }
}

}

The distinct method in this test case does not return a distinct list. The assert for count will fail. I looked at other similar questions and Microsoft examples but they look exactly like the code I have in the test. Any input?

  • Are you sure your .Equals method is being called, and it is not using the == operator? – BlackICE Jan 25 '12 at 05:29
  • [This post](http://stackoverflow.com/questions/1365748/distinct-not-working-with-linq-to-objects) covers much of the same information. – Adam S Jan 25 '12 at 05:46

2 Answers2

3

You also need to override the GetHashCode() and Equals methods from the Object class. For more information see this corresponding FXCOP violation.

Then your tests will work like expected.

public class SampleObject : IEquatable<SampleObject>
{
    public int Id { get; set; }
    public string Name { get; set; }

    public bool Equals(SampleObject other)
    {
        if (ReferenceEquals(this, other))
            return true;
        if (ReferenceEquals(other, null) || ReferenceEquals(this, null))
            return false;

        return Id.Equals(other.Id);
    }

    public override int GetHashCode() 
    {
        return Id;
    }

    public override bool Equals(object obj) 
    {
        return Equals(obj as SampleObject);
    }
}
m0sa
  • 10,712
  • 4
  • 44
  • 91
  • 1
    for completeness, one should probably also override Object.Equals (next to implementing IEquatable) – jeroenh Jan 25 '12 at 07:10
0

You're implementing an interface without defining both members to the specification of the interface. Your code needs to look like this:

    public bool Equals(SampleObject x, SampleObject y)
    {
        if (ReferenceEquals(x, y))
            return true;
        if (ReferenceEquals(x, null) || ReferenceEquals(y, null))
            return false;

        return x.Id.Equals(y.Id);
    }

    public int GetHashCode(SampleObject obj)
    {
               public int GetHashCode(SampleObject obj)
    {
        if (Object.ReferenceEquals(obj, null)) return 0;
        int hashId = obj.Id == null ? 0 : obj.Id.GetHashCode();
        int hashName = obj.Name == null ? 0 : obj.Name.GetHashCode();
        return hashId ^ hashName; // or what ever you want you hash to be, hashID would work just as well.
    }
    }
Seabiscuit
  • 18
  • 2
  • 2
    GetHashCode must not depend on Name, because Name is not used in determining Equality. For any two objects that are equal, GetHashCode must return the same value. Furthermore, obj.Id will never be null, because it is an int. – phoog Jan 25 '12 at 06:06