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

时间:2009-06-19 01:56:23

标签: c# .net 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"

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

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:定义了扩展方法'FunnyExtension.Double(int)'   on value type'int'不能用于创建委托。

他们为什么不能?

2 个答案:

答案 0 :(得分:18)

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

  

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

考虑到这一点,我得到了一个新答案。试试这个尺寸:

结构上的普通“实例”方法在CIL级别采用“托管指针”(类型&)作为接收器参数。这是必要的,以便结构上的实例方法可以分配给结构的字段。请参阅Partition II, Section 13.3

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

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

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

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

答案 1 :(得分:2)

编辑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没有太大帮助。 documentation for CreateDelegate也没什么帮助。我确定它与装箱接收器有关,也许知道Rotor源的人可能有助于解释原因吗?