Почему ключевое слово '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 -->

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

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

Обновление

Как Бен Робинсон говорит, синтаксис для вызова методов расширения - это просто сахара-компилятор. Тогда почему компилятор не может автоматически проверять методы расширения для существующих типов базового типа, не требуя этого ключевого слова?

Ответ 1

Пара точек:

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

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

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

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

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

Ответ 2

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

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

против.

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

Ответ 3

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

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

Вы могли бы сказать: "Почему он просто не понимает, что является вызывающим объектом и передает это как параметр?"

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

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

Ответ 4

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

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

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

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

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

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

Ответ 5

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

Ответ 6

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