views:

1409

answers:

4

I'm seeing different behavior between using .Equals and == between two of .NET 4.0's new Tuple<> instances. If I have overridden Equals on the object in the Tuple<> and call .Equals on the Tuples the override of Equals will be called. If I use == on the Tuples the override of Equals is not called. Is that by design and does it make sense?

EDIT: From answers and comments I can tell I'm not being clear. I know Tuple<> is a reference type and that for reference types == will check identity (ReferenceEquals). But, should Tuple<> override == to check equality of the objects it contains? For consistency, probably not.

For example if I have a simple object

public class NameAndNumber
{
    public int Number { get; set; }
    public string Name { get; set; }

    public override bool Equals(object obj)
    {
        if (obj is NameAndNumber)
        {
            NameAndNumber other = (NameAndNumber)obj;
            return Number == other.Number && Name == other.Name;
        }

        return false;
    }
}

and then I do something like this:

Tuple<NameAndNumber, NameAndNumber> left = new Tuple<NameAndNumber, NameAndNumber>(
      new NameAndNumber { Name = "one", Number = 1 }, 
      new NameAndNumber { Name = "two", Number = 2 });
Tuple<NameAndNumber, NameAndNumber> right = new Tuple<NameAndNumber, NameAndNumber>(
      new NameAndNumber { Name = "one", Number = 1 }, 
      new NameAndNumber { Name = "two", Number = 2 });
bool operatorResult = left == right;
bool equalsResult = left.Equals(right);
Console.Out.WriteLine("operatorResult = {0}  equalsResult = {1}", 
        operatorResult, equalsResult);

I get operatorResult = false equalsResult = true

Should I be expecting that?

I know the implementation of Equals on NameAndNumber isn't "right" it's just simplified sample code.

I have also tried implementing IEquatable, ==, !=, and GetHashCode. Same results.

+3  A: 

For Reference Type: == performs an identity comparison, i.e. it will only return true if both references point to the same object. While Equals() method is expected to perform a value comparison, i.e. it will return true if the references point to objects that are equivalent.

For reference types where == has NOT been overloaded, it compares whether two references refer to the same object

J.W.
Except for string which is magic. So I guess I'm looking for more magic. Would you expect a Tuple of value types to do value checking? It doesn't.
Mike Two
There is no magic - it simply has overloaded ==/!= operators; you can see this in reflector as op_Equality and op_Inequality (on System.String). There **is**, however, magic on things like `int`/`float` (defined in the spec) and `Nullable<T>` (which uses "lifted" operators).
Marc Gravell
I am not sure that I fully understand your comments, your Tuple in your example code is a reference type, isn't it.
J.W.
@Marc Gravell. Sorry the magic comment was not clear. I know how it works, thanks for clarifying it. The overrides on string make it behave differently than most other BCL objects which then leads me to sometimes think == will work on other reference types magically. Which of course leads me to ask questions about Tuples that I should have figured out myself.
Mike Two
@J.W. Sorry I'm really not being clear this morning. I'm completely jet lagged and shouldn't be asking questions. I was really trying to point out what some others have said. That == will do identity comparison unless you override it. Saying magic was a poor choice of words and has added confusion. It wasn't meant to indicate that your answer did not apply to the case I pointed out. Thanks.
Mike Two
+1  A: 

By default, the operator == tests for reference equality, so Yes the result you are seeing is expected.

See Guidelines for Overriding Equals() and Operator == (C# Programming Guide):

In C#, there are two different kinds of equality: reference equality (also known as identity) and value equality. Value equality is the generally understood meaning of equality: it means that two objects contain the same values. For example, two integers with the value of 2 have value equality. Reference equality means that there are not two objects to compare.

Mitch Wheat
A: 

By default, == (on a class) means reference equality; i.e. are they the same instance; what object.ReferenceEquals(x,y) would return.

You can provide your own == / != operators to get the expected behaviour - and when you override Equals it is important to override GetHashCode too (otherwise you break usage as a key - Why is it important to override GetHashCode when Equals method is overriden in C#?):

public static bool operator == (NameAndNumber x, NameAndNumber y) {
    if (x == null && y == null) return true;
    if (x == null || y == null) return false;
    return x.Number == y.Number && x.Name == y.Name;
    // or if polymorphism is important: return x.Equals(y);
}
public static bool operator !=(NameAndNumber x, NameAndNumber y) {
    return !(x == y); // lazy but works
}
public override int GetHashCode() {
    return (Name == null ? 0 : Name.GetHashCode()) +
        17 * Number.GetHashCode();
}
Marc Gravell
I know. As I mentioned I showed a simplified implementation and left GetHashCode out for a smaller example. But that will not change how Tuple works and I cannot add those to Tuple. I can derive from Tuple and add them.
Mike Two
If polymorphism is the issue, then make the == use `.Equals` - other than that... this is simply how `==` works. If you don't overload it, it won't work this way!
Marc Gravell
@Marc Gravell. I guess the heart of the question lies in should the built in Tuple class in .net 4.0 behave this way or not. Should it act like a standard reference type or delegate equality checking to the objects it contains? It is just a container so should we care if the two containers are the same or should == check the containers contents. This isn't my implementation of Tuple, it is the built in .net 4 one. Previous third party libraries with a Tuple or Pair implementation would override == check the equality of the contained objects.
Mike Two
Ah, until the most recent edit the question didn't make it clear that you were talking about .NET 4.0s's `Tuple<...>` types. In fairness, I missed the tag though ;-p
Marc Gravell
@Marc Gravell. Sorry about that. I've tried to make it more clear, and learned not to rely on the tag.
Mike Two
+4  A: 

The results you see come from a design compromise, Tuples are now shared between F# and C#. The main point is that all Tuples are indeed implemented as reference types, that was not so obvious.

The decision whether Tuples should do deep or shallow equality checks was moved to two interfaces: IStructuralComparable, IStructuralEquatable. Note that those 2 are now also implemented by the Array class.

Henk Holterman