CSharpFunctionalExtensions icon indicating copy to clipboard operation
CSharpFunctionalExtensions copied to clipboard

ValueObject.CompareTo handles variable number of components incorrectly

Open josefblaha opened this issue 4 years ago • 3 comments

I encountered something similar like in #237, but it turned out to be related to variable number of equality components. Consider the following class:

class ListLike : ValueObject
{
    string[] components;
    	
    public ListLike(params string[] components)
    {
        this.components = components;
    }

    protected override IEnumerable<object> GetEqualityComponents()
    {
        return components;
    }
}

Using CompareTo will give these results:

var a = new ListLike("one", "two");
var b = new ListLike("one", "two", "three");
	
a.CompareTo(b) // => 0
b.CompareTo(a) // throws IndexOutOfRangeException

The first call compares only prefix of the two lists. The second call fails on expectation of same number of equality components.

I wonder if there is any generic way of comparing lists of variable length. Maybe lexicographic order would work?

josefblaha avatar Feb 22 '21 18:02 josefblaha

Yes, you need to include the number of elements in the collection into the equality components:

private class VOWithCollection : ValueObject
{
    readonly string[] _components;

    public VOWithCollection(params string[] components)
    {
        _components = components;
    }

    protected override IEnumerable<object> GetEqualityComponents()
    {
        yield return _components.Length;
        foreach (string component in _components)
        {
            yield return component;
        }
    }
}

vkhorikov avatar Feb 23 '21 02:02 vkhorikov

This indeed works. But IMHO it's not very intuitive, that you need to implement your value object this way. What do you think about using lexicographical order like this? https://github.com/josefblaha/CSharpFunctionalExtensions/commit/bff63760d594608d7cbd37f1822613f4bcffb2ad

josefblaha avatar Mar 14 '21 20:03 josefblaha

Yes, your idea is indeed better. It can be simplified, though: just compare the lengths of components and otherComponents before going into the for loop. The rest can be kept as-is.

vkhorikov avatar Mar 20 '21 03:03 vkhorikov