为什么重载决议在这里不起作用?

Why doesn't overload resolution work here?

考虑这个片段:

var bytes = new byte[] {0, 0, 0, 0};
bytes.ToList().ForEach(Console.WriteLine);

这将导致编译时错误:

No overload for 'System.Console.WriteLine(int)' matches delegate 'System.Action<byte>'

您可以将 lamdba 声明为解决方法,bytes.ToList().ForEach((b => Console.WriteLine(b))),但为什么在第一种情况下重载解析不起作用?

简单地说,签名不匹配,因为 Console.WriteLine(byte).

没有重载

lambda 示例之所以有效,是因为您提供了一种方法,该方法需要满足签名的 byte,然后将其传递给 Console.WriteLine(这将隐式处理 byte) .

您的 Foreach 方法将被推断为 Foreach(Action<byte> action)

没有采用单个参数 byteWriteLine 重载,因此无法编译。

What's up with that? Why can't it compile with Console.WriteLine(int)?

因为 Action<int>Action<byte>

不兼容

来自 C# 语言规范

15.2 委托兼容性:

如果满足以下所有条件,则方法或委托 M 与委托类型 D 兼容:

  • D和M的参数个数相同,D中的每个参数与M中对应的参数具有相同的ref或out修饰符。
  • 对于每个值参数(没有 ref 或 out 修饰符的参数),存在身份转换(§6.1.1)或隐式引用转换(§6.1.6)从D中的参数类型到M中对应的参数类型。
  • 对于每个ref或out参数,D中的参数类型与M中的参数类型相同。
  • 存在从 M 的 return 类型到 D 的 return 类型的标识或隐式引用转换。

过载解决方案在 identity conversion (§6.1.1) or implicit reference conversion (§6.1.6) 存在时失败; None 其中存在于此处。 Byte 也没有到 int 的身份转换或隐式引用转换。因此,它无法编译为 Console.WriteLine(int).

Why can't it compile with Console.WriteLine(int)?

因为Action<T>在类型参数T和contravariance doesn't work for value types上是逆变的。如果它是其他引用类型,它会编译为 Console.WriteLine(object) 因为逆变确实适用于引用类型。

例如:

Action<int> action1 = Console.WriteLine;//Compiles to Console.WriteLine(int)
Action<byte> action2 = Console.WriteLine;//Won't compile
Action<StringBuilder> action3 = Console.WriteLine;//Compiles to Console.WriteLine(object)

如您所见,即使 Console.WriteLine(StringBuilder) 没有重载,Action<StringBuilder> 也会编译;这是因为引用类型支持逆变。