Reputation: 24606
Assuming the following delegate "caller" signature:
FuncCaller<T>(Func<T, bool> predicate)
and a matching method:
bool MyFunc(object o)
When T
is a reference type, I can invoke MyFunc
implicitly like so:
FuncCaller<String>(MyFunc) // valid
Conversely, when T
is a value type, I get a compilation error when calling MyFunc implicitly:
FuncCaller<Int32>(MyFunc) // invalid ("No overload for 'MyFunc(object)' matches delegate 'System.Func<int?,bool>'")
My question is, given these two examples, why is the call to MyFunc
invalid when invoked implicitly, but valid when invoked explicitly like so:
FuncCaller<Int32>(i => MyFunc(i)) // valid
I assume this is some kind of issue related to boxing and unboxing of types?
Upvotes: 2
Views: 969
Reputation:
Yes, that's exactly what it is.
FuncCaller<Int32>(i => MyFunc(i)) // valid
creates an anonymous function, with a parameter of type Int32
, and a return value of bool
, which does nothing but convert the parameter to object
, and calls MyFunc
. It's as if you'd written
FuncCaller<Int32>(MyFunc2)
where MyFunc2
is defined as
bool MyFunc2(Int32 o) { return MyFunc(o); }
Converting the parameter is a no-op and therefore unnecessary when the type is a reference type, which is why a function with a parameter type of object
is close enough for a delegate with a parameter type of string
: the parameter type is already an object
.
Converting the parameter is a real operation that cannot be bypassed when the type is a value type.
It's worth pointing out that
FuncCaller<Int32>(MyFunc)
isn't ever shorthand for
FuncCaller<Int32>(i => MyFunc(i))
even though it may behave almost the same way. The former passes the MyFunc
function directly, the latter creates a wrapper function around it. That wrapper function is necessary here.
Upvotes: 6