4

I'm using Visual Studion 2017 version 15.5.2, and C# version 7.2. To the point:

Color c = default;                              // or: c = default(Color); no difference
Debug.Print($"{c.Equals(default(Color))}");     // true
Debug.Print($"{c.Equals(default)}");            // false WHY?!

But if I use ValueTuple:

(string s, int i) t = default;                  
Debug.Print($"{t.Equals(default((string, int)))}"); // true
Debug.Print($"{t.Equals(default)}");                // true

Is it supposed to be like this?

BJ Myers
  • 6,617
  • 6
  • 34
  • 50
lisz
  • 435
  • 5
  • 9

6 Answers6

6

Is this Windows Forms?

Because in WinForms, System.Drawing.Color.Equals() doesn't have an overload that takes a Color. Instead, it only has the one from Object. In WPF, System.Windows.Media.Color.Equals() contains an overload that takes a Color.

When default is passed as an argument to Color.Equals(Object), what gets passed is default(Object) since the compiler infers Object to be the type based on its signature. From the docs:

The default literal produces the same value as the equivalent default(T) where T is the inferred type.

Clearly, default(Color) isn't equivalent to default(Object), since Color is a value type and Object is a reference type (which defaults to null).

ValueTuple.Equals(), on the other hand, takes another ValueTuple, so the compiler has no trouble inferring the type of default as default(ValueTuple).

Edit:

As of .NET Core 2.0, System.Drawing.Color.Equals() does have an overload that takes a Color. The compiler would have no trouble inferring the type of default as default(Color); therefore, it would now return true.

GxSilver
  • 3
  • 3
BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • I was wrong about what "default expression" realy means. This is clear for me now, thanks. – lisz Jan 16 '18 at 17:40
  • @lisz Like lambda expressions, `default` is one of the few cases in C# where type information flows from the type of the assignment target to the type of the thing being assigned. In your example, the assignment target is a method parameter, so the type information comes from there. – Mike Strobel Jan 16 '18 at 17:48
  • @Mike Strobel I got confused, because I somehow thought c.Equals(default) should be equivalent to "c == default" (where c is System.Drawing.Color) - but they are completely different things. – lisz Jan 16 '18 at 18:05
5

@fharreau is correct: System.Drawing.Color does not implement an Equals(Color) method, so $"{t.Equals(default)}" binds to the only method available: Equals(Object). Thus, default resolves to default(Object) or null.

If you use System.Windows.Media.Color from WPF, which does implement Equals(Color), then you'll see the expected results:

System.Windows.Media.Color c = default;
Console.WriteLine($"{c.Equals(default(System.Windows.Media.Color))}");  // true
Console.WriteLine($"{c.Equals(default)}");                              // true

ValueTuple also provides an Equals to compare against another tuple, which is why you saw the expected result.

Mike Strobel
  • 25,075
  • 57
  • 69
5

In the first code block, the .Equals() method comes from the base object class, meaning the default will be the default value for object and not Color. This is why it returns false.

The tuple .Equals() method however has been overriden to take the relevant type, the internals of that function compare the individual components. From the docs, a ValueTuple is considered equal if:

  • Its components are of the same types as those of the current instance.
  • Its components are equal to those of the current instance. Equality is determined by the default equality comparer for each component.
DavidG
  • 113,891
  • 12
  • 217
  • 223
4

No, this is as expected.

default provides the default value for the type of parameter or variable or whatnot it is assigned to.

So let's look at this:

Color c = Color.White;
bool b = c.Equals(x);

What type is x expected to be? since the Color type does not declare a Equals method that takes Color, the only method available is this:

public class Object
{
    public virtual bool Equals(object obj)
    ...

So the default here will be expected to fulfill the need of object, not Color, hence your code is actually this:

bool b = c.Equals(default(object));

which is the same as this:

bool b = c.Equals(null);

But hang on, why then is this true?

bool b = c.Equals(default(Color));

? That is because Color overrides the Equals method from object, or the base method knows how to compare value types, that still takes object as parameter, and thus your other statement:

bool b = c.Equals(default(Color));

actually provides a Color, and not null, and that's why the two are different.

Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
3

This result matches the documentation, but the documentation for the second (Tuple) snippet doesn't say what you might expect.

The Color.Equals() method accepts an argument of type Object. So you're comparing the default color to the default Object. Those are not the same, hence the false result.

ValueType.Equals() has overloads to accept arguments for both Object and ValueTuple. The documentation here is interesting... the version with the ValueTuple argument always returns true. The Object version returns true if the argument is a ValueTuple, or false otherwise. In other words, according to the Equals method, all ValueTuples are equal to each other, but not equal to anything that isn't a ValueTuple.

The only other trick here is how the default keyword is interpreted in this context. We can clearly see here that you're getting a default ValueTuple, rather than a default Object. I'm not up to date on exactly why or how the compiler is able to determine this from the context, but once you have that value for the argument it's easy to see why you get the true result in this case. Clearly overload resolution would then choose the ValueTuple of the method, which (again) always returns true.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
0

System.Drawing.Color structure does not implement IEquatable whereas ValueTuple does.

So in first case compiler selects Object.Equals

Pavel Voronin
  • 13,503
  • 7
  • 71
  • 137
  • IEquatable is irrelevant. He's calling .Equals() explicitly. – Joel Coehoorn Jan 16 '18 at 20:44
  • @JoelCoehoorn Have you ever met struct with `Equals(T other)`, which did not implement `IEquatable`? – Pavel Voronin Jan 16 '18 at 22:14
  • I didn't say it doesn't implement IEquatable. I said it's irrelevant whether it does or not, because the code in the question is calling .Equals() directly, and types are able to overload Equals() method without implementing IEquatable. – Joel Coehoorn Jan 16 '18 at 22:39