Your Foreach method will be inferred as Foreach(Action<byte> action)
.
There is no WriteLine
overload which takes a single parameter byte
and thus it doesn't compile.
What's up with that? Why can't it compile with
Console.WriteLine(int)
?
Because Action<int>
isn't compatible to Action<byte>
From C# language specification
15.2 Delegate compatibility:
A method or delegate M is compatible with a delegate type D if all of the following 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 with no ref or out modifier), an identity conversion (§6.1.1) or implicit reference conversion (§6.1.6) exists from the parameter type in D to the corresponding parameter type in M.
- For each ref or out parameter, the parameter type in D is the same as the parameter type in M.
- An identity or implicit reference conversion exists from the return type of M to the return type of D.
Overload resolution fails at an identity conversion (§6.1.1) or implicit reference conversion (§6.1.6) exist; None of them exist here. Byte doesn't have identity conversion to int or implicit reference conversion either. So, it can't compile to Console.WriteLine(int)
.
Why can't it compile with Console.WriteLine(int)
?
Because Action<T>
is contravariant on type parameter T and contravariance doesn't work for value types. If it were some other reference type, it would have compiled to Console.WriteLine(object)
because contravariance do work with 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>
does compile even though there is no overload of Console.WriteLine(StringBuilder)
; It is because reference types supports contravariance.