扩展方法与静态方法优先级

时间:2012-03-28 18:27:39

标签: c# extension-methods static-methods

考虑以下计划:

class A
{
    public static void Foo()
    {
    }
}

static class Ext
{
    public static void Foo(this A a)
    {
    }
}

class Program
{
    static void Main(string[] args)
    {
        var a = new A();
        a.Foo();
    }
}

编译失败,错误:

  

使用实例引用无法访问成员'Test.A.Foo()';使用类型名称来限定它

为什么编译器会忽略扩展方法?

3 个答案:

答案 0 :(得分:5)

您尝试做的事情是不允许的。 C# MSDN Extension Method Article明确指出:

  

您可以使用扩展方法来扩展类或接口,但不能覆盖它们。永远不会调用与接口或类方法具有相同名称和签名的扩展方法。在编译时,扩展方法的优先级始终低于类型本身中定义的实例方法。

谢天谢地,这是不允许的,因为必须要保持这一点。


编辑:所以人们都说静态方法不是实例方法,这是正确的。但试着这样做:

class A
{
   public static void Foo() {}
   public void Foo() {}
}

由于名称含糊不清,它不会编译。如果允许您使用扩展方法,那就是会发生什么。它会引入完全相同的歧义。现在,假设一个方法是静态的,一个是实例,那么这应该意味着没有歧义。但在目前的状态下,它确实引入了歧义,这也是为什么它不被允许的另一个原因。

编辑#2:来自评论@ErenErsonmez:

  

但是,只要扩展方法与实例方法没有相同的签名,我就不会理解它如何通过静态方法引起歧义

如果您更改扩展方法的签名,它肯定会起作用。所以以下内容将起作用:

class A
        {
            public static void Foo() { }
        }

    static class Ext
    {
        public static void Foo(this A me, int i)
        { }
    }
    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            a.Foo(10);

            Console.ReadLine();
        }
    }

所以它看起来更像是一个含糊不清的问题而不是一个与已经存在的方法同名的扩展方法。

答案 1 :(得分:3)

this MSDN article显示这是出于安全问题。

  

我经常听到可以使用扩展方法的担忧   劫持或颠覆现有方法的预期行为。视觉   基本通过尽可能确保实例来确保这一点   方法优于扩展方法。

     

该语言允许使用扩展方法来创建重载   对于具有不同签名的现有实例方法。这允许   扩展方法用于创建重载,同时防止   来自被覆盖的现有实例方法。如果是扩展方法   存在与实例方法相同的签名,阴影   编译器内置的规则将更喜欢实例   方法,因此消除了扩展方法的可能性   覆盖现有的基类实例功能

这是以VB为重点(并以实例为重点),但仍然是一般的想法。基本上,扩展方法采用最低优先级,因此方法不能被劫持,并且由于类已经有一个方法签名用于您尝试执行的操作,因此优先级会引发标准扩展方法错误(当尝试从实例对象)。你永远不可能有两个具有相同签名的方法,这就是你要求在这里尝试的......并且如上所述,允许它将是一个安全问题。

然后,添加由此创建的混淆,允许它只是一个坏主意。

答案 2 :(得分:3)

问题是重载解决方案:静态方法Foo()是候选者,它是适用的 - 只是选择它,因为最佳匹配会导致错误 - 这正是发生的事情。在考虑所有其他候选人之后,扩展方法仅是重载解决的候选者。在OPs问题的情况下,在错误发生之前甚至不会考虑扩展方法。