代码之家  ›  专栏  ›  技术社区  ›  SLaks

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

  •  29
  • SLaks  · 技术社区  · 15 年前

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

    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值类型“int”不能用于创建委托。

    他们为什么不能?

    2 回复  |  直到 11 年前
        1
  •  18
  •   Doug McClean    15 年前

    对于我的另一个回答,埃里克·史密斯正确地指出:

    “……”因为它需要隐式装箱接收程序类型参数…。不管怎样,如果你这样做:func f=5.toString;这是完全合法的。

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

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

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

    自CIL以来 & S和 o S可以由指针(和)实现,对于委托实现来说,一切都是轻而易举的。不管委托是捕获静态方法、类实例方法还是结构实例方法,它所需要做的就是将指针传递给 _target 函数的第一个参数。

    但我们正在讨论的场景是废墟。一种静态扩展方法,采用 int 作为第一个参数,需要类型为的CIL参数 int32 (见第三部分第1.1.1节)。 这里是事情偏离轨道的地方。 我看不出有什么理由 可能的 使委托的实现认识到发生了这种情况(例如,通过检查与要捕获的MethodInfo关联的元数据),并发出一个thunk,该thunk将取消对 靶标 把它作为第一个论点,但是 对于结构上的经典实例方法的委托,这是不需要的,因为它们无论如何都需要一个指针。 并且不会出现(根据我之前错误答案中的例子判断)要实现的情况。显然,所讨论的特定值类型将控制所需thunk的确切性质。

    除非我缺少一个更基本的实现障碍(例如,我可以想象它会给验证器带来问题),否则似乎可以合理地扩展运行时来支持这种情况,但所有的迹象都表明这是运行时的限制,而不是C编译器本身的限制。

        2
  •  2
  •   Doug McClean    15 年前

    编辑2 我不再相信这个答案了,但我把它留在这里,这样线程仍然有意义,这样人们就会明白为什么它是不正确的。关于这件事的不同看法,请看我的另一个答案。

    原件

    因为它需要隐式装箱值类型Receiver参数(因为包含Receiver参数的System.Delegate类型是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();
        }
    }
    

    在调用CreateDelegate时,得到一个ArgumentException:“错误绑定到目标方法”。我不知道为什么,因为相关的方法是 internalcall 方法,反射镜没什么帮助。这个 documentation for CreateDelegate 也没什么帮助。我敢肯定这和装箱的接收器有关,也许有人知道转子源可以帮助解释为什么?

    推荐文章