Как вызвать метод расширения из собственного класса без кастинга?

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

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();

Любые объяснения?

Ответ 1

Приведение не требуется - часть this. Так что это прекрасно работает:

return this.HelloWorld();

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

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

Этот вызов:

HelloWorld()

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

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

Ответ 2

this.HelloWorld(); работает с без литья.

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

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

Ответ 3

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

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

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

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

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

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

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

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

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