Search code examples
c#boxinganonymous-methodsunboxing

Boxing & Unboxing: Why doesn't this delegate match?


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?


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.