c# 7.2 默认表达式和等于(错误?)

c# 7.2 default expression and Equals (bug?)

我使用的是 Visual Studio 2017 版本 15.5.2 和 C# 版本 7.2。重点:

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

但是如果我使用 ValueTuple:

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

应该是这样的吗?

这是Windows表格吗?

因为在 WinForms 中,System.Drawing.Color.Equals() 没有采用 Color 的重载。相反,它只有 the one from Object. In WPF, System.Windows.Media.Color.Equals() contains an overload that takes a Color.

default 作为参数传递给 Color.Equals(Object) 时,传递的是 default(Object),因为编译器推断 Object 是基于其签名的类型。来自 docs:

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

显然,default(Color) 不等同于 default(Object),因为 Color 是值类型而 Object 是引用类型(默认为 null)。

ValueTuple.Equals(),另一方面,takes another ValueTuple,因此编译器可以毫不费力地将 default 的类型推断为 default(ValueTuple).

编辑:

从 .NET Core 2.0 开始,System.Drawing.Color.Equals() 确实有一个需要 Color 的重载。编译器可以毫不费力地将默认类型推断为 default(Color);因此,它现在 return 为真。

@fharreau 是正确的:System.Drawing.Color 没有实现 Equals(Color) 方法,因此 $"{t.Equals(default)}" 绑定到唯一可用的方法:Equals(Object)。因此,default 解析为 default(Object)null.

如果您使用 WPF 中的 System.Windows.Media.Color 实现了 Equals(Color),那么您将看到预期的结果:

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

ValueTuple 还提供了一个 Equals 来与另一个元组进行比较,这就是您看到预期结果的原因。

不,这符合预期。

default 为参数或变量的类型或分配给它的诸如此类的东西提供默认值。

让我们看看这个:

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

x 应该是什么类型?由于 Color 类型没有声明采用 ColorEquals 方法,唯一可用的方法是:

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

所以这里的 default 应该满足 object 的需要,而不是 Color,因此你的代码实际上是这样的:

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

与此相同:

bool b = c.Equals(null);

但等等,为什么这是真的?

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

?这是因为 Color 覆盖了 来自 objectEquals 方法,基本方法知道如何比较值类型,它仍然以 object 作为参数,因此您的其他语句:

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

实际上提供了一个Color,而不是null,这就是两者不同的原因。

此结果与文档相符,但第二个(元组)片段的文档并未说明您可能期望的内容。

Color.Equals() 方法接受类型为 Object 的参数。因此,您正在将默认颜色与默认对象进行比较。那些不一样,因此 false 结果。

ValueType.Equals() 有重载来接受 ObjectValueTuple 的参数。这里的文档很有趣... 带有 ValueTuple 参数的版本总是 returns true。对象版本 returns 如果参数是 ValueTuple,则为 true,否则为 false。换句话说,根据 Equals 方法,所有 ValueTuple 彼此相等,但不等于任何不是 ValueTuple 的值。

这里唯一的其他技巧是如何在此上下文中解释 default 关键字。我们可以在这里清楚地看到您正在获取默认的 ValueTuple,而不是默认的对象。我不是最新的 whyhow 编译器能够从上下文中确定这一点,但是一旦你有了这个值很容易看出为什么在这种情况下会得到 true 结果。显然,重载解析会选择方法的 ValueTuple,它(再次)总是 returns true。

在第一个代码块中,.Equals() 方法来自基础 object class,这意味着 default 将是 [=11= 的默认值] 而不是 Color。这就是它 returns 错误的原因。

然而,元组 .Equals() 方法已被覆盖以采用相关类型,该函数的内部比较各个组件。从 docs 开始,如果满足以下条件,则认为 ValueTuple 相等:

  • 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.

System.Drawing.Color 结构不实现 IEquatableValueTuple 实现。

所以在第一种情况下编译器选择 Object.Equals