6

If I have a default interface method like this:

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

Can I have my concrete implementation call that default method?

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!!");
    }
}

So that calling:

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

Results in this:

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

Indeed Calling C# interface default method from implementing class shows that I can call method that my class does not implement, but as somewhat expected adding call to ((IGreeter)this).SayHello(name); inside HappyGreeter.SaysHello causes stack overflow.

Alexei Levenkov
  • 96,782
  • 12
  • 124
  • 169
D. Patrick
  • 2,674
  • 24
  • 32
  • Let me ask yo ua stupid question: if you could not call the default method, how would it EVER be called? – TomTom Jun 11 '20 at 21:59
  • I don't think that's a stupid question at all. This is my first time working with default interface members. But, the way I would call that is by casting my concrete instance as the interface with the default member. It's pretty cool. I've often wanted to see traits in C# and I think this is going to be a powerful addition once I get used to it. – D. Patrick Jun 11 '20 at 22:02
  • Duplicate. t his acutally is not stupid and quite tricky - but also answered somewhere else. Interesting. – TomTom Jun 11 '20 at 22:05
  • Is it possible to un-close it or do I need to re-ask it? Thanks a lot for taking the time to moderate the community. In this case, I think it was probably mis-labeled. I looked pretty hard for an answer to this question. – D. Patrick Jun 11 '20 at 22:10
  • `Can I have my concrete implementation call that default method?` I don't believe so, although there looks to be plans to add that through `base`. https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/default-interface-methods – mjwills Jun 11 '20 at 22:16
  • This can be achieved by inheriting from the class or abstract class, where you can access "default" methods via `base.method()` – Fabio Jun 11 '20 at 22:21
  • @Fabio Can you show us how that would look? I can't picture it. – mjwills Jun 11 '20 at 22:24
  • @mjwills, _by inheriting from the class or abstract class with virtual methods_ instead of interface. – Fabio Jun 11 '20 at 22:26
  • @Fabio Oh you mean without using interfaces *at all*. That wasn't entirely clear on your comment (due to use of "default"). I read it as having an interface, an implementing class then one inheriting from it. – mjwills Jun 11 '20 at 22:27
  • @D.Patrick You can declare in the inteface `IGreeter` a static method that implements functionality of the method `SayHello`. And then this method can be call from the class `HappyGreeter`. Do you consider such approach? – Iliar Turdushev Jun 12 '20 at 02:23
  • @IliarTurdushev oh, that's a fun idea!! – D. Patrick Jun 12 '20 at 02:38

3 Answers3

4

As far as I know you can't invoke default interface method implementation in inheriting class (though there were proposals). But you can call it from inheriting interface:

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

In my original answer too much wanted to show that you can call base in inheriting interface, but as @Alexei Levenkov suggested in comments cleaner way in this particular case would be something like this:

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!!");
    }
} 
Guru Stron
  • 42,843
  • 5
  • 48
  • 70
2

There is a very simple way to handle this:

  1. Declare the default method as static. Don't worry, you will still be able to override it in a class that inherits from it.
  2. Call the default method using the same type of syntax when calling a static method of a class, only substitute the interface name for the class name.

This code applies to C#8 or later, and it won't work if building against the .NET Framework. I ran it on Windows 10 with C#9, running on .NET 6, preview 5.

Example:

public interface IGreeter
{
   private static int DisplayCount = 0;
   public static void SayHello(string name)
   {
      DisplayCount++;
      Console.WriteLine($"Hello {name}! This method has been called {DisplayCount} times.");
   }
}

public class HappyGreeter : IGreeter
{
   public void SayHello(string name)
   {
      // what can I put here to call the default method?
      IGreeter.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("Pippy");
      hg.SayHello("Bob");
      hg.SayHello("Becky");
   }
}

Example Output:

Hello Pippy! This method has been called 1 times.
I hope you're doing great!!
Hello Bob! This method has been called 2 times.
I hope you're doing great!!
Hello Becky! This method has been called 3 times.
I hope you're doing great!!

Static fields are also now allowed in interfaces as this example shows.

If you can't use a static interface method for some reason, then you can always rely on the following technique:

  1. Put the default method implementation code into a separate static method.
  2. Call this static method from the default implementation method.
  3. Call this static method at the top of the class implementation of the interface method.

Example:

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");
   }
}

Sample Output:

Hello Bob!
I hope you're doing great!!
Bob Bryan
  • 3,487
  • 1
  • 29
  • 42
1

I know that this is not an answer to the question, but the next approach also can be used to emulate base functionality:

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!!");
    }
}
Iliar Turdushev
  • 4,360
  • 1
  • 6
  • 19