拳击和拆箱:为什么这个代表不匹配?

Nat*_*lor 2 c# boxing unboxing anonymous-methods

假设以下代理"调用者"签名:

FuncCaller<T>(Func<T, bool> predicate)
Run Code Online (Sandbox Code Playgroud)

和匹配方法:

bool MyFunc(object o)
Run Code Online (Sandbox Code Playgroud)

什么时候T是引用类型,我可以像这样MyFunc 隐式调用:

FuncCaller<String>(MyFunc) // valid
Run Code Online (Sandbox Code Playgroud)

相反,如果T是值类型,则在隐式调用MyFunc时会出现编译错误:

FuncCaller<Int32>(MyFunc) // invalid ("No overload for 'MyFunc(object)' matches delegate 'System.Func<int?,bool>'")
Run Code Online (Sandbox Code Playgroud)

我的问题是,鉴于这两个例子,为什么MyFunc在隐式调用时调用无效,但在明确调用时有效如下:

FuncCaller<Int32>(i => MyFunc(i)) // valid
Run Code Online (Sandbox Code Playgroud)

我认为这是与拳击和拆箱类型相关的某种问题?

小智 6

是的,这正是它的本质.

FuncCaller<Int32>(i => MyFunc(i)) // valid
Run Code Online (Sandbox Code Playgroud)

创建一个匿名函数,其参数类型Int32和返回值bool,除了将参数转换为object和调用之外什么都不做MyFunc.这就像你写的那样

FuncCaller<Int32>(MyFunc2)
Run Code Online (Sandbox Code Playgroud)

在哪里MyFunc2定义为

bool MyFunc2(Int32 o) { return MyFunc(o); }
Run Code Online (Sandbox Code Playgroud)

转换参数是一个无操作,因此当类型是引用类型时是不必要的,这就是参数类型object为足够接近参数类型的委托的函数的原因string:参数类型已经object.

转换参数是一种实际操作,当类型为值类型时,无法绕过该操作.

值得指出的是

FuncCaller<Int32>(MyFunc)
Run Code Online (Sandbox Code Playgroud)

永远不会简写

FuncCaller<Int32>(i => MyFunc(i))
Run Code Online (Sandbox Code Playgroud)

即使它可能表现得几乎相同.前者MyFunc直接传递函数,后者在它周围创建一个包装函数.这里需要包装函数.