C# Compare Lists with custom object but ignore order

旧街凉风 提交于 2019-12-24 12:14:23

问题


I'm trying to compare 2 Lists (wrapped in an object) containing custom objects. I don't care about the order, but if list 1 contains "1,2,3,4" then list 2 must and only contain those elements. E.g.: "4,2,3,1"

Based on Compare two List<T> objects for equality, ignoring order ignoring-order I've used the Except and Any but it doesn't give me the desired results.

If I use Assert.Equals it fails, but Assert.IsTry(list1.equals(list2)) succeeds.

Further more if I remove the Equals and GetHashCode implementation then both tests fail.

public class AppointmentCollection : List<Appointment>
{
    public override bool Equals(object obj)
    {            
        var appCol = obj as AppointmentCollection;

        if (appCol == null)
        {
            return false;
        }

        return (appCol.Count == this.Count) && !(this.Except(appCol).Any());
    }


    public override int GetHashCode()
    {
        unchecked
        {
            //use 2 primes
            int hash = 17;
            foreach (var appointment in this)
            {
                hash = hash * 19 + appointment.GetHashCode();
            }
            return hash;
        }
    }
}

public class Appointment
{
    public string Title {get; set;}
    public DateTime StartTime {get; set;}
    public DateTime EndTime { get; set;}

    public override bool Equals(object obj)
    {
        var appointment = obj as Appointment;
        if (appointment == null)
        {
            return false;
        }
        return Title.Equals(appointment.Title) &&
            StartTime.Equals(appointment.StartTime) &&
            EndTime.Equals(appointment.EndTime);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            //use 2 primes
            int hash = 17;
            hash = hash * 19 + Title.GetHashCode();
            hash = hash * 19 + StartTime.GetHashCode();
            hash = hash * 19 + EndTime.GetHashCode();
            return hash;
        }
    }
}

[Test]
public void TestAppointmentListComparisonDifferentOrder()
{
    var appointment1 = new Appointment(
        "equals test1",
        new DateTime(2013, 9, 4),
        new DateTime(2013, 9, 4));

    var appointment2 = new Appointment(
        "equals test2",
        new DateTime(2013, 9, 4),
        new DateTime(2013, 9, 4));

    var list1 = new AppointmentCollection() { appointment1, appointment2 };
    var list2 = new AppointmentCollection() { appointment2, appointment1 };

    //With Equals/GetHashCode in AppointmentCollection implemented
    CollectionAssert.AreEqual(list1, list2); //fails
    Assert.IsTrue(list1.Equals(list2)); //success

    //Without Equals/GetHashCode in AppointmentCollection implemented
    CollectionAssert.AreEqual(list1, list2); //fails
    Assert.IsTrue(list1.Equals(list2)); //fails
}

回答1:


You didn't state clearly which unit test tool you use. Maybe CollectionAssert is the class Microsoft.VisualStudio.TestTools.UnitTesting.CollectionAssert, or maybe it is NUnit.Framework.CollectionAssert, or maybe something else?

So check the documentation of your testing tool, or write here which one you use.

However, it is common for

CollectionAssert.AreEqual( ... );

to check if the collections are the same in the same order, while

CollectionAssert.AreEquivalent( ... );

will check what you want. So use the latter.

Neither of the two methods on CollectionAssert actually uses your override of Equals(object). To use that, write:

Assert.AreEqual( ... );

Edit: I thought Assert.AreEqual(exp, act); would always end up doing exp.Equals(act) which would call your override on AppointmentCollection. But it turns out we end in the private instance method EqualConstraint.ObjectsEqual, and as one sees it checks if the run-time type implements ICollection in which case your override is never used.

Lesson learned: Using Assert.AreEqual can be confusing with collections. Use CollectionAssert.AreEquivalent or CollectionAssert.AreEqual to make your intention clear. You don't have to override Equals on AppointmentCollection if you only need it for testing. If you need it for the application itself and you want to test that, write the test with list1.Equals(list2) literally to make sure your own override is what is tested.

(In any case the override on Appointment is needed, of course.)



来源:https://stackoverflow.com/questions/18632069/c-sharp-compare-lists-with-custom-object-but-ignore-order

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!