shekharpro / mb-unit

Automatically exported from code.google.com/p/mb-unit
0 stars 0 forks source link

Structural equality comparers #424

Closed GoogleCodeExporter closed 8 years ago

GoogleCodeExporter commented 8 years ago
As a developer I want to be able to assert on the equality of two objects
that do not define Equals (and have no reason to) and that may have
multiple properties.  Given a map of the relevant object structure MbUnit
should compose an appropriate equality comparer itself.

Motivating Example:

public class Entity
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Value { get; set; }
    public Child[] Children { get; set; }
}

// n.b. ignores Id
Assert.AreEquals(object1, object2, new StructuralEqualityComparer<Entity>()
{
    // compare Name case-insensitively
    { x => x.Name, (x, y) => string.Compare(x, y, true) },

    // compare Value using default comparison
    { x => x.Value },

    // compare Children recursively
    {
        x => x.Children,
        new StructuralEqualityComparer<Child>()
        {
            // more of the same...
        }
    },

    // add some other arbitrary binary predicates over the objects
    // silly example here
    { (x, y) => x.GetHashCode() == y.GetHashCode() }
});

Skeleton Implementation:

// n.b. should use this instead of Func<T, T, bool> elsewhere in MbUnit
public delegate bool EqualityComparison<T>(T x, T y);

public delegate TValue Accessor<T, TValue>(T object);

public class StructuralEqualityComparer : IEqualityComparer<T>
{
    private readonly List<Predicate<T>> predicates = new List<Predicate<T>>();

    public void Add<TValue>(Accessor<T, TValue> accessor)
    {
        if (accessor == null) throw...

        predicates.Add((x, y) =>
ComparisonSemantics.Equals<TValue>(accessor(x), accessor(y)));
    }

    public void Add<TValue>(Accessor<T, TValue> accessor,
EqualityComparison<TValue> comparer)
    {
        if (accessor == null) throw...
        if (comparer == null) throw...

        predicates.Add((x, y) => comparer(accessor(x), accessor(y)));
    }

    public void Add<TValue>(Accessor<T, TValue> accessor,
IEqualityComparer<TValue> comparer)
    {
        if (accessor == null) throw...
        if (comparer == null) throw...

        predicates.Add((x, y) => comparer.Equals(accessor(x), accessor(y)));
    }

    public void Add<TValue>(EqualityComparison<T> comparer)
    {
        if (comparer == null) throw...

        predicates.Add(comparer);
    }

    public void Add<TValue>(IEqualityComparer<T> comparer)
    {
        if (comparer == null) throw...

        predicates.Add(comparer.Equals);
    }

    public bool Equals(T x, T y)
    {
        return predicates.TrueForAll(p => p(x, y));
    }

    public int GetHashCode(T x)
    {
        return 0;
    }
}

I'm sure this can be improved.  Think about it.

Original issue reported on code.google.com by jeff.br...@gmail.com on 17 Apr 2009 at 6:38

GoogleCodeExporter commented 8 years ago
Oh my... This is black magic :0

Original comment by Yann.Tre...@gmail.com on 17 Apr 2009 at 7:16

GoogleCodeExporter commented 8 years ago

Original comment by Yann.Tre...@gmail.com on 21 Apr 2009 at 6:26

GoogleCodeExporter commented 8 years ago
StructureEqualityComparer<T> implemented in v3.0.7.

Original comment by Yann.Tre...@gmail.com on 25 Apr 2009 at 8:52