11

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
  • 98,904
  • 14
  • 127
  • 179
D. Patrick
  • 2,894
  • 26
  • 37
  • 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://learn.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

5

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 I was to much focused on showing that you can call base one in an inheriting interface, but as @Alexei Levenkov suggested in comments cleaner way in this particular case would be something like the following:

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
  • 102,774
  • 10
  • 95
  • 132
  • 1
    That is disgusting. And genius. – mjwills Jun 11 '20 at 22:31
  • 1
    @mjwills agreed on the first part))) – Guru Stron Jun 11 '20 at 22:31
  • 2
    It would be nice if I could use `base(IGreeter).SayHello(name)`. – D. Patrick Jun 11 '20 at 23:52
  • 1
    Nice. Note that you don't need the extra interface if you just want to call the default implementation `class WorkAround: IGrreeter { } ... ((IWorkAround)new WorkAround()).SayHello(name);` – Alexei Levenkov Jun 20 '20 at 00:47
  • In my test I get an endless loop and stackoverflow with this – Rubix Cube Jan 09 '23 at 12:23
  • @RubixCube can you please share a [mre] (I suppose you either casting something wrong or have a cycle in methods). Cause [this works](https://sharplab.io/#v2:C4LgTgrgdgNAJiA1AHwAICYCMBYAUHqAUwHcACACQEMAHagTwHExDDhCwAKASgDoBlSnXKEANiID2HAERsAzsClcA3HjyoAzKQCWUNmABmlAMaFSASSYs9eAN55SD0qgAspAUNESOqTAAZSUJQAtoRcpAC8AHxOmDw+AJwcACRSwmLipDaBIQC+AISKKrg5qrgaTugUNPSWrOykIOa11rh2uI6k1GBaAG6UbBWkAOriYADWAIJg4tBwDU3MdWCZpCXtjl29/aY+AGykzJRw4lAidAtW9QD6xKOT07MRASTDd1MzUHDcRfaO5S5uQRpLw+fzZUK/BxtDodG5vB6ffhAzyScHKSEwnxxTCJKRmUgAC3E1FMdBmAHJmKRjjoAOakWmHYB5Aro9YONY5UhAA) as it was at the moment of writing. – Guru Stron Jan 09 '23 at 12:26
  • Hmm i think there is a slight difference in my case that may be the cause: I have something like this (so i'm implementing explicitly an higher interface function): ``` public interface IGreeter { void SayHello(string name) } public interface IBaseGreeter { void IGreeter.SayHello(string name) => System.Console.WriteLine($"Hello {name}!"); } public interface IWorkAround : IBaseGreeter { public void IGreeter.SayHello(string name) { (this as IBaseGreeter).SayHello(name); System.Console.WriteLine("I hope you're doing great!!"); } } ``` – Rubix Cube Jan 10 '23 at 14:33
3

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,687
  • 1
  • 32
  • 45
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,935
  • 1
  • 10
  • 23