Почему ключевое слово "this" требуется для вызова метода расширения из расширенного класса


Я создал метод расширения для ASP.NET MVC ViewPage, например:

public static class ViewExtensions
{
    public static string Method<T>(this ViewPage<T> page) where T : class
    {
        return "something";
    }
}

при вызове этого метода из представления (извлечение из ViewPage), Я получаю ошибку "CS0103: имя "метод" не существует в текущем контексте" если я использую this ключевое слово, чтобы назвать это:

<%: Method() %> <!-- gives error CS0103 -->
<%: this.Method() %> <!-- works -->

почему ключевое слово? Или это работает без него, но я что-то упускаю?

(Я думаю, что должен быть дубликат этого вопрос, но я не смог найти)

обновление:

как

6 71

6 ответов:

пара моментов:

во-первых, предлагаемая функция (неявное "это."на вызов метода расширения) is лишних. Методы расширения были необходимы для понимания запросов LINQ, чтобы работать так, как мы хотели; получатель всегда указывается в запросе, поэтому нет необходимости поддерживать неявное это, чтобы заставить LINQ работать.

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

Если вы находитесь в сценарии, где вы используете метод расширения для типа в рамках этого типа затем do иметь доступ к исходному коду. почему вы используете метод расширения в первую очередь тогда? можно писать метод самостоятельно, если у вас есть доступ к исходному коду расширенного типа, а затем вам не нужно использовать метод расширения вообще! Затем ваша реализация может воспользоваться доступом к частному состоянию объекта, что невозможно для методов расширения.

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

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

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

// without 'this'
string s = ViewExtensions.Method(page);

и

// with 'this'
string s = page.Method();

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

методы расширения являются статическими. Позвонив Method(), а не this.Method() или Method(this), вы не говорите компилятору, что передать методу.

вы можете сказать: "почему он просто не понимает, что такое вызывающий объект, и не передает это как параметр?-

ответ заключается в том, что методы расширения являются статическими и могут быть названы из статического контекста, где нет "этого".

Я думаю, что они могли бы проверить это во время компиляции, но, честно говоря, это, вероятно, много работы для очень небольшой отдачи. И, честно говоря, я вижу мало пользы в том, чтобы убрать некоторые из явных вызовов метода расширения. Тот факт, что они могут быть ошибочными, например, методами, означает, что они могут быть довольно неинтуитивными время от времени (например, NullReferenceExceptions не выбрасываются). Я иногда думаю, что они должны ввели новый оператор стиля "pipe-forward" для методов расширения.

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

я приведу вам пример другого отличия: довольно легко вызвать метод расширения на null ссылка на объект. К счастью, это гораздо сложнее сделать с помощью обычных методов. (Но это можно сделать. IIRC, Джон Скит продемонстрировал, как это сделать, манипулируя CIL код.)

static void ExtensionMethod(this object obj) { ... }

object nullObj = null;
nullObj.ExtensionMethod();  // will succeed without a NullReferenceException!

это, как говорится, Я согласен, что это кажется немного нелогичным, что this требуется для вызова метода расширения. В конце концов, метод расширения должен в идеале "чувствовать" и вести себя так же, как нормальный.

потому что метод расширения не существует с классом ViewPage. Вам нужно сообщить компилятору, что вы вызываете метод расширения. Помните this.Method() Это просто компилятор сахар для ViewExtensions.Method(this). Точно так же вы не можете просто вызвать метод расширения в середине любого класса по имени метода.

Я работаю над свободным API и столкнулся с той же проблемой. Несмотря на то, что у меня есть доступ к классу, который я расширяю, я все еще хотел, чтобы логика каждого из методов fluent была в их собственных файлах. Ключевое слово "this" было очень неинтуитивным, пользователи продолжали думать, что метод отсутствует. То, что я сделал, это сделать мой класс частичным классом, который реализовал необходимые мне методы вместо использования методов расширения. Я не видел упоминания о партиалах в ответах. Если у вас есть этот вопрос partials может быть a более лучший вариант.