Как вызвать метод расширения из собственного класса без приведения? - PullRequest
10 голосов
/ 19 марта 2012

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

public interface IHelloWorld
{
}

public static class Extensions
{
    public static string HelloWorld(this IHelloWorld ext)
    {
        return "Hello world!";
    }
}

public class Test : IHelloWorld
{
    public string SaySomething()
    {
        return HelloWorld();
    }
}

В основном я расширяю интерфейс.Я продолжаю получать эту ошибку:

The name 'HelloWorld' does not exist in the current context

Может кто-нибудь объяснить мне это?Когда я снимаюсь, все выглядит хорошо:

return ((Test)this).HelloWorld();

Есть объяснения?

Ответы [ 3 ]

21 голосов
/ 19 марта 2012

приведение не является необходимым - this часть.Так что это прекрасно работает:

return this.HelloWorld();

В разделе 7.6.5.2 явно говорится о вызовах методов в форме

expr.identifier ( )
expr.identifier ( args )
expr.identifier < typeargs > ( )
expr.identifier < typeargs > ( args )

Этот вызов:

HelloWorld()

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

Мне не сразу понятно почему язык был разработан таким образом (т.е. почему исключено "неявное это") и, возможно, Эрик Липпертдобавлю ответ на этот счет позже.(Ответ вполне может быть таким: «потому что для спецификации, реализации и тестирования потребовалось бы много времени, чтобы получить сравнительно небольшие выгоды».) Однако этот ответ по крайней мере показывает, что компилятор C # придерживается спецификации...

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

this.HelloWorld(); работает с без приведения .

Вспомните, как работают методы расширения:

Вы используете объект, и компилятор узнает тип, а затем может разрешить егок методу расширения.Если объект не используется, он не сможет его разрешить.

1 голос
/ 19 марта 2012

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

Давайте рассмотрим следующий пример, который, на мой взгляд, довольно распространен:

public class DoubleSet : List<double>
{
    public IEnumerable<double> Square()
    {
        return this.Select( x => x*x );
    }
}

ЭтоСовершенно верно, что this не является необходимым для компилятора для правильной интерпретации метода Select.

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

Для читателя кода ясно, что метод расширения будет обрабатывать экземпляр this, как если бы он ничего не знал о его внутреннем состоянии.И действительно, класс объекта совершенно неизвестен методу расширения (поскольку метод расширения знает только интерфейс)

Если бы код был только:

    public IEnumerable<double> Square()
    {
        return Select( x => x*x );
    }

, это было бы намного меньшеОчевидно, что вы имеете дело с IEnumerable.Select, который фактически вызывает IList.GetEnumerator и заставляет каждый элемент один за другим вызывать функцию x => x * x.

...