为什么重载决策不在这里工作?

jdp*_*nix 3 c# generics overloading type-inference covariance

请考虑以下代码段:

var bytes = new byte[] {0, 0, 0, 0};
bytes.ToList().ForEach(Console.WriteLine);
Run Code Online (Sandbox Code Playgroud)

这将导致编译时错误:

No overload for 'System.Console.WriteLine(int)' matches delegate 'System.Action<byte>'
Run Code Online (Sandbox Code Playgroud)

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

Sri*_*vel 5

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

没有WriteLine重载需要一个参数byte,因此它不能编译.

那是怎么回事?为什么不能编译 Console.WriteLine(int)

因为Action<int>不兼容Action<byte>

来自C#语言规范

15.2代表兼容性:

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

  • D和M具有相同数量的参数,并且D中的每个参数具有与M中的对应参数相同的ref或out修饰符.
  • 对于每个值参数(没有ref或out修饰符的参数),从D中的参数类型到M中的相应参数类型存在标识转换(第6.1.1节)或隐式引用转换(第6.1.6节).
  • 对于每个ref或out参数,D中的参数类型与M中的参数类型相同.
  • 从返回类型M到返回类型D存在标识或隐式引用转换.

身份转换(第6.1.1节)隐式引用转换(第6.1.6 节)存在时,重载解析失败; 这里没有一个存在.字节也没有到int或隐式引用转换的标识转换.所以,它无法编译到Console.WriteLine(int).

为什么不能编译Console.WriteLine(int)

因为Action<T>类型参数T是逆变的,并且逆变量对值类型不起作用.如果它是一些其他引用类型,它将编译为,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)
Run Code Online (Sandbox Code Playgroud)

正如你所看到的Action<StringBuilder>那样,即使没有重载也会编译Console.WriteLine(StringBuilder); 这是因为参考类型支持逆变.

  • 我不确定我是否看到协方差/逆变与隐式转换之间的关系.`int`和`byte`之间没有继承. (4认同)