请考虑以下代码段:
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)))
,但为什么在第一种情况下不会重载解析工作?
答案 0 :(得分:5)
您的Foreach方法将推断为Foreach(Action<byte> action)
。
没有WriteLine
重载,只需要一个参数byte
,因此无法编译。
这是怎么回事?为什么不能用它编译
Console.WriteLine(int)
?
因为Action<int>
与Action<byte>
来自C#语言规范
15.2代表兼容性:
如果满足以下所有条件,则方法或委托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>