Почему ключевое слово «this» требуется для вызова метода расширения из расширенного класса - PullRequest
74 голосов
/ 18 августа 2010

Я создал метод расширения для 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? Или это работает без него, но я что-то упустил?

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

Обновление

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

Ответы [ 6 ]

48 голосов
/ 18 августа 2010

Пара баллов:

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

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

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

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

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

7 голосов
/ 18 августа 2010

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

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

против

// with 'this'
string s = page.Method();
6 голосов
/ 18 августа 2010

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

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

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

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

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

3 голосов
/ 18 августа 2010

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

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

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

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

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

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

1 голос
/ 18 августа 2010

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

0 голосов
/ 09 сентября 2017

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

...