Метод расширения по сравнению со статическим методом - PullRequest
11 голосов
/ 28 марта 2012

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

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 ]

5 голосов
/ 28 марта 2012

То, что вы пытаетесь сделать, запрещено. В статье о методе расширения MSDN C # конкретно указано, что:

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

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


РЕДАКТИРОВАТЬ: Итак, люди говорят, что статические методы не являются методами экземпляра, и это правильно. Но попробуйте сделать это:

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 голосов
/ 28 марта 2012

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

3 голосов
/ 28 марта 2012

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

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

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

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

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

...