Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Any good reason why Tuple.Equals does not check exact types?

Tags:

c#

Failure to check for type can lead to un-symmetric equality:

public sealed class MyClass : Tuple<string>
{
    private readonly int _b;

    public MyClass(string a, int b) : base(a)
    {
        _b = b;
    }

    public override bool Equals(object obj)
    {
        return Equals(obj as MyClass);
    }

    private bool Equals(MyClass obj)
    {
        if (obj == null) return false;
        return base.Equals(obj) && obj._b == _b;
    }
}

[Test]
public void Show_broken_symmetric_equality()
{
    Tuple<string> a = Tuple.Create("Test");
    var b = new MyClass("Test", 3);
    Assert.AreEqual(a, b);
    Assert.AreNotEqual(b, a);
}

That test passes, but it should not, it shows the symmetric property of a well implemented Equals is broken.

Looking at the code for Tuple that's because Tuple doesn't check the concrete types match, i.e. there is no equivalent of GetType() == obj.GetType(). It checks assignability, with an is check, but doesn't compare types.

There's nothing I can do in MyClass to fix this situation, because the incorrect line is Assert.AreEqual(a, b); which is the call to Tuple.Equals. And, as juharr points out, changing MyClass.Equals to return true in this case would break transitivity.

Long shot, but I wonder if anyone knows a good reason why it has been implemented in such a way? Or why it was not sealed if it was implemented this way.

like image 844
weston Avatar asked Feb 04 '16 12:02

weston


1 Answers

No, I have looked at this before and as far as I understand there is no good reason why they don't check the type properly (except for that they got it wrong from the start and then it's of course impossible to change it).

Every MSDN advice on best practice for equals talks about doing "GetType() != obj.GetType()" to make sure the types are exactly the same, but equals in Tuple only does a cast with 'as' operator which will (as you noticed) give unexpected result and disable the ability for derived classes to adhere to best practices for equals.

IMHO - Don't derive from Tuple, and definitely don't implement equals if you do it.

like image 197
Niclas Avatar answered Sep 19 '22 17:09

Niclas