Метод расширения и статический метод

Рассмотрим следующую программу:

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()" не может быть доступен с ссылкой на экземпляр; квалифицируйте его вместо имени типа

Почему компилятор игнорирует метод расширения?

Ответ 1

Проблема заключается в разрешении перегрузки: статический метод Foo() является кандидатом, он применим - просто выбор его, поскольку наилучшее совпадение вызовет ошибку - это именно то, что происходит. Методы расширения - это только кандидаты на разрешение перегрузки после рассмотрения всех других кандидатов. В случае проблемного случая OPs метод расширения даже не был рассмотрен до возникновения ошибки.

Ответ 2

То, что вы пытаетесь сделать, не допускается. В С# MSDN Extension Method Article говорится, что:

Вы можете использовать методы расширения для расширения класса или интерфейса, но не для их переопределения. Метод расширения с тем же именем и сигнатурой как интерфейс или метод класса никогда не будет вызван. Во время компиляции методы расширения всегда имеют более низкий приоритет, чем методы экземпляров, определенные в самом типе.

Слава богу, это не допустимо, так как это было бы ужасно, что нужно поддерживать.


EDIT: люди говорят, что статические методы - это не методы экземпляра, которые верны. Но попробуйте сделать это:

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();
        }
    }

Итак, это больше похоже на то, что проблема неоднозначна, а не то, что никогда не может быть метод расширения с тем же именем, что и уже существующий метод.

Ответ 3

Из этой статьи MSDN видно, что это связано с проблемами безопасности.

Я часто слышал о том, что методы расширения могут использоваться для захватить или подорвать предполагаемое поведение существующих методов. визуальный Основные адреса этого, гарантируя, что, когда это возможно, экземпляр метод предпочтительнее по методу расширения.

Язык позволяет использовать методы расширения для создания перегрузок для существующих методов экземпляра с разными сигнатурами. Это позволяет методы расширения, которые будут использоваться для создания перегрузок, существующий метод экземпляра от переопределения. Если метод расширения существует с той же сигнатурой, что и метод экземпляра, затенение правила, встроенные в компилятор, предпочтут экземпляр метод, поэтому исключение возможности метода расширения переопределение существующей функции экземпляра базового класса

Это сосредоточено на VB (и сосредоточено на экземпляре), но все же существует общая идея. В принципе, метод расширения принимает наименьший приоритет, поэтому методы не могут быть захвачены, и поскольку у класса уже есть подпись метода для того, что вы пытаетесь сделать, это имеет приоритет и выдает ошибку стандартного метода расширения (при попытке вызвать из экземпляр объекта). У вас никогда не может быть двух методов с одной и той же подписью, и именно поэтому вы просите, чтобы здесь были предприняты попытки... и позволили бы решить проблему безопасности, как уже объяснялось выше.

Затем добавьте путаницу, которая будет создана этим, и это просто плохая идея, чтобы разрешить это.