在值类型上定义的扩展方法不能用于创建委托 - 为什么不呢?

SLa*_*aks 31 .net c# extension-methods delegates value-type

可以将扩展方法分配给与对象的用法匹配的委托,如下所示:

static class FunnyExtension {
    public static string Double(this string str) { return str + str; }
    public static int Double(this int num) { return num + num; }
}


Func<string> aaMaker = "a".Double;
Func<string, string> doubler = FunnyExtension.Double;

Console.WriteLine(aaMaker());       //Prints "aa"
Console.WriteLine(doubler("b"));    //Prints "bb"
Run Code Online (Sandbox Code Playgroud)

如果他们扩展的类型是值类型,它将不起作用:

Func<int> eightMaker = 4.Double;    //Error CS1113: Extension methods 'FunnyExtension.Double(int)' defined on value type 'int' cannot be used to create delegates
Func<int, int> intDoubler = FunnyExtension.Double;  //Works
Run Code Online (Sandbox Code Playgroud)

这给了

错误CS1113:在值类型"int"上定义的扩展方法'FunnyExtension.Double(int)'不能用于创建委托.

他们为什么不能?

Dou*_*ean 18

在回答我的另一个答案时,Eric Smith正确地指出:

"...因为它需要隐式装箱接收器类型参数......".无论如何会发生什么,如果你做这样的事情:Func f = 5.ToString; 这是完全合法的.

考虑到这一点,我得到了一个新的答案.试试这个尺码:

结构上的普通"实例"方法在CIL级别采用"托管指针"(类型&)作为接收器参数.这是必要的,以便结构上的实例方法可以分配给结构的字段.见第II部分,第13.3节.

类似地,类上的实例方法将"对象引用"(类型O)作为接收器参数(不同之处在于这是指向托管堆的指针,需要为GC跟踪).

由于CIL &Os都可以(并且是)通过指针实现,因此对于委托实现来说,一切都是hunky-dory.无论委托是否捕获静态方法,类实例方法或结构实例方法,它所需要做的就是将指针传递给_target函数的第一个参数.

但我们正在讨论的情景破坏了这一点.采用int第一个参数的静态扩展方法需要类型的CIL参数int32(参见第III部分,第1.1.1节).这是事情发生的地方.我没有看到任何理由为什么代理的实现不可能意识到这种情况正在发生(例如,通过检查与被捕获的MethodInfo相关联的元数据)并发出一个将取消打包_target并传递的thunk 作为第一个参数,但对于结构体上的经典实例方法的委托来说,这不是必需的,因为它们无论如何都期望指针并且不会出现(通过我之前的错误答案中的示例来判断).显然,所讨论的特定值类型将控制所需thunk的确切性质.

除非我错过了一个更基本的实现障碍(例如,我可以想象它会给验证器带来问题),似乎可以通过扩展运行时来支持这种情况,但所有的迹象都是合理的.指向这是运行时的限制,而不是C#编译器本身的限制.

  • 道格,你的分析非常好.正如我们通过电子邮件讨论的那样,我的同事Sreekar在他的博客中做了一些笔记http://blogs.msdn.com/sreekarc/archive/2009/06/25/why-can-t-extension-methods-on-value- type-be-curried.aspx我已经在我的博客中记录了这个问题是如何通过"反思currying"出现的.http://blogs.msdn.com/ericlippert/archive/2009/06/25/mmm-curry.aspx (5认同)