Может ли класс C# вызвать метод интерфейса по умолчанию из собственной реализации?

Если у меня есть такой метод интерфейса по умолчанию:

public interface IGreeter
{
    void SayHello(string name) => System.Console.WriteLine($"Hello {name}!");
}

Можно ли в моей конкретной реализации вызвать этот метод по умолчанию?

public class HappyGreeter : IGreeter
{
    public void SayHello(string name)
    {
        // what can I put here to call the default method?
        System.Console.WriteLine("I hope you're doing great!!");
    }
}

Так что призыв:

var greeter = new HappyGreeter() as IGreeter;
greeter.SayHello("Pippy");

Результатов в этом:

// Hello Pippy!
// I hope you're doing great!!

Действительно, вызов метода интерфейса C# по умолчанию из реализации класса показывает, что я могу вызвать метод, который не реализует мой класс, но, как несколько ожидалось, добавление вызова к((IGreeter)this).SayHello(name); внутри HappyGreeter.SaysHello вызывает переполнение стека.

3 ответа

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

public class HappyGreeter : IGreeter
{
    private interface IWorkAround : IGreeter
    {
        public void SayHello(string name)
        {
            (this as IGreeter).SayHello(name);
            System.Console.WriteLine("I hope you're doing great!!");
        }
    }

    private class WorkAround : IWorkAround {}

    public void SayHello(string name)
    {
        ((IWorkAround)new WorkAround()).SayHello(name);
    }
}

UPD

В моем первоначальном ответе очень хотелось показать, что вы можете позвонить baseпри наследовании интерфейса, но, как предложил Alexei Levenkov в комментариях, более чистый способ в этом конкретном случае будет примерно таким:

public class HappyGreeter : IGreeter
{
    private class WorkAround : IGreeter { }
    private static readonly IGreeter _workAround = new WorkAround();

    public void SayHello(string name)
    {
        _workAround.SayHello(name);
        System.Console.WriteLine("I hope you're doing great!!");
    }
} 

Есть довольно простой способ справиться с этим:

  1. Поместите код реализации метода по умолчанию в отдельный статический метод.
  2. Вызовите этот статический метод из метода реализации по умолчанию.
  3. Вызовите этот статический метод в верхней части реализации класса метода интерфейса.

Пример:

      public class DefaultMethods
{
   // This class is used to show that a static method may be called by a default interface method.
   //
   public static void SayHello(string name) => Console.WriteLine($"Hello {name}!");
}

public interface IGreeter
{
   void SayHello(string name) => DefaultMethods.SayHello(name);
}

public class HappyGreeter : IGreeter
{
   public void SayHello(string name)
   {
      // what can I put here to call the default method?
      DefaultMethods.SayHello(name);
      Console.WriteLine("I hope you're doing great!!");
   }
}

public class CS8NewFeatures
{
   // This class holds the code for the new C# 8 features.
   //
   public void RunTests()
   {
      TestGreeting();
   }

   private void TestGreeting()
   {
      // Tests if a default method may be called after a class has implemented it.
      //
      var hg = new HappyGreeter();
      hg.SayHello("Bob");
   }
}

Пример вывода:

      Hello Bob!
I hope you're doing great!!

Я знаю, что это не ответ на вопрос, но следующий подход также можно использовать для имитации base функциональность:

public interface IGreeter
{
    void SayHello(string name) => BaseSayHello(name);

    // This static method can be used in implementers of "IGreeter"
    // to emulate "base" functionality.
    protected static void BaseSayHello(string name) => System.Console.WriteLine($"Hello {name}!");
}

public class HappyGreeter : IGreeter
{
    public void SayHello(string name)
    {
        IGreeter.BaseSayHello(name);
        Console.WriteLine("I hope you're doing great!!");
    }
}
Другие вопросы по тегам