为什么重载决议在这里不起作用?
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)
。
没有采用单个参数 byte
的 WriteLine
重载,因此无法编译。
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>
也会编译;这是因为引用类型支持逆变。
考虑这个片段:
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)
。
没有采用单个参数 byte
的 WriteLine
重载,因此无法编译。
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>
也会编译;这是因为引用类型支持逆变。