Why is overload not working here?

Consider this snippet:

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

      

This will result in a compile-time error:

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

      

You can declare lamdba as a workaround bytes.ToList().ForEach((b => Console.WriteLine(b)))

, but why not overload the permission in the first case?

+3


source to share


2 answers


Your Foreach method will be output as Foreach(Action<byte> action)

.

There is no overload WriteLine

that takes a single parameter byte

and therefore does not compile.

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

?

Because it is Action<int>

not compatible withAction<byte>

From the C # language specification

15.2 Delegate Compatibility:

A method or delegate M is compatible with the type of delegate D if all of them are true:



  • D and M have the same number of parameters, and each parameter in D has the same ref or out modifiers as the corresponding parameter in M.
  • For each value parameter (a parameter without the ref or out modifier) , an identity conversion (ยง6.1.1) or implicit reference conversion (ยง6.1.6) is performed from the parameter type in D to the corresponding parameter type in M.
  • For each ref or out parameter, the type of the parameter in D is the same as the type of the parameter in M.
  • There is an identifier or implicit reference conversion from return type M to return type D.

Overload resolution fails on identity conversion (ยง6.1.1) or implicit reference conversion (ยง6.1.6) ; None of them exist here. Byte has no identity conversion for int or implicit reference conversion. Thus, it cannot compile Console.WriteLine(int)

.

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

?

Because it Action<T>

is contravariant with respect to the type of the parameter T, and contravariance does not work for value types . If it were some other reference type, it would compile with Console.WriteLine(object)

, because contravariance works on a reference type.

For example:

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)

      

As you can see, Action<StringBuilder>

compiles even without overloading Console.WriteLine(StringBuilder)

; This is because reference types support contravariance.

+5


source


Simply put, the signatures do not match as there is no overload for Console.WriteLine(byte)

.



The lambda example works because you provide a method that expects byte

that matches the signature and then passes it to Console.WriteLine

(which will implicitly handle byte

).

+3


source







All Articles