在值类型上定义的扩展方法不能用于创建委托
可以将扩展方法分配给与它们在对象上的使用相匹配的代理,如下所示:
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"
如果它们扩展的类型是值类型,它将不起作用:
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
这给了
错误CS1113:值类型'int'上定义的扩展方法'FunnyExtension.Double(int)'不能用于创建委托。
他们为什么不能呢?
为了回应我的其他答案,Eric Smith正确地指出:
“...因为它需要隐式地装箱接收器类型参数......”。 无论如何,如果你这样做:Func f = 5.ToString; 这是完全合法的。
思考这个问题让我得到了一个新的答案。 试试这个大小:
结构上的普通“实例”方法在CIL级别采用“受管指针”(类型&
)作为接收者参数。 这是必要的,以便结构上的实例方法可以分配给结构体的字段。 见分区II,第13.3节。
同样,类的实例方法将“对象引用”(类型O
)作为接收器参数(区别在于这是指向托管堆的指针,需要跟踪GC)。
由于CIL &
s和O
s都可以(并且)通过指针实现,所以对于委托实现来说,一切都很顺利。 无论代理是捕获一个静态方法,一个类实例方法还是一个结构实例方法,它所需要做的就是将指向它的_target
的指针传递给该函数的第一个参数。
但是我们正在讨论的场景就是废墟。 以int
为第一个参数的静态扩展方法需要int32
类型的CIL参数(请参见分区III,第1.1.1节)。 这里是事情发生的地方。 我没有看到任何理由说明代理实现不可能意识到发生了这种情况(例如,通过检查与被捕获的MethodInfo相关联的元数据)并发出一个将解包_target
的thunk和把它作为第一个参数传递,但这对于委托给结构上的经典实例方法并不需要,因为它们无论如何都期望一个指针,并且不会出现(根据我之前不正确答案中的例子来判断)。 显然,具体的值类型将控制所需thunk的确切性质。
除非我错过了实现的一个更基本的障碍(例如,我可以想象这会对验证者造成问题),似乎可以合理地将延长运行时间以支持这种情况,但所有符号都是指向这是运行时限制,而不是C#编译器本身。
编辑2我不相信这个答案了,但我把它留在这里,所以线程仍然有意义,这样人们会明白为什么它不正确。 看到我的另一个答案,就是不同的看法。
原版的
因为它需要隐式装箱值接收器参数(因为保存接收器参数的System.Delegate类型中的_target字段是System.Object类型的),如果您不期待,这可能会导致一些奇怪的别名行为它。
编辑
这里还有别的事情要做。 我运行了这个示例程序:
class Program
{
public static int Combine(int a, int b)
{
return a + b;
}
static void Main(string[] args)
{
var combineMethod = typeof(Program).GetMethod("Combine");
var add4 = Delegate.CreateDelegate(
typeof(Converter<int, int>),
4,
combineMethod) as Converter<int, int>;
for (int i = 0; i < 10; i++)
{
Console.WriteLine(add4(i));
}
Console.ReadLine();
}
}
并得到一个ArgumentException:“错误绑定到目标方法。” 在CreateDelegate的调用中。 我不知道为什么,因为相关的方法是一个internalcall
调用方法,Reflector没有太大的帮助。 CreateDelegate的文档也没有太大的帮助。 我相信它与装箱接收器有关,也许有人知道Rotor源可以帮助解释为什么?
上一篇: Extension methods defined on value types cannot be used to create delegates