在值类型上定义的扩展方法不能用于创建委托

可以将扩展方法分配给与它们在对象上的使用相匹配的代理,如下所示:

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源可以帮助解释为什么?

链接地址: http://www.djcxy.com/p/52875.html

上一篇: Extension methods defined on value types cannot be used to create delegates

下一篇: Returning a default value. (C#)