Question

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?

Was it helpful?

Solution

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top