为什么没有超载解决方案在这里工作?

时间:2015-05-10 08:57:46

标签: c# generics covariance type-inference overloading

请考虑以下代码段:

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))),但为什么在第一种情况下不会重载解析工作?

2 个答案:

答案 0 :(得分: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修饰符的参数),标识转换(第6.1.1节)或隐式引用转换(第6.1.6节)存在来自参数类型D到M。
  • 中相应的参数类型
  • 对于每个ref或out参数,D中的参数类型与M中的参数类型相同。
  • 从M的返回类型到D的返回类型存在标识或隐式引用转换。

identity conversion (§6.1.1)implicit reference conversion (§6.1.6)存在时,重载解析失败;这里没有一个存在。字节也没有到int或隐式引用转换的标识转换。因此,它无法编译为Console.WriteLine(int)

  

为什么不能用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)

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

答案 1 :(得分:3)

简单地说,签名不匹配,因为Console.WriteLine(byte)没有重载。

lambda示例有效,因为您提供的方法需要满足签名的byte,然后将其传递给Console.WriteLine(将隐式处理byte)。< / p>