Can a C# class call an interface's default interface method from its own implementation?
Asked Answered
G

3

14

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.

Grebe answered 11/6, 2020 at 21:57 Comment(11)
Let me ask yo ua stupid question: if you could not call the default method, how would it EVER be called?Rubenstein
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.Grebe
Duplicate. t his acutally is not stupid and quite tricky - but also answered somewhere else. Interesting.Rubenstein
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.Grebe
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. learn.microsoft.com/en-us/dotnet/csharp/language-reference/…Lindane
This can be achieved by inheriting from the class or abstract class, where you can access "default" methods via base.method()Condign
@Condign Can you show us how that would look? I can't picture it.Lindane
@mjwills, by inheriting from the class or abstract class with virtual methods instead of interface.Condign
@Condign 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.Lindane
@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?Selsyn
@IliarTurdushev oh, that's a fun idea!!Grebe
S
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!!");
    }
} 
Shrove answered 11/6, 2020 at 22:27 Comment(7)
That is disgusting. And genius.Lindane
@Lindane agreed on the first part)))Shrove
It would be nice if I could use base(IGreeter).SayHello(name).Grebe
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);Hyder
In my test I get an endless loop and stackoverflow with thisNealneala
@RubixCube can you please share a minimal reproducible example (I suppose you either casting something wrong or have a cycle in methods). Cause this works as it was at the moment of writing.Shrove
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!!"); } } ```Nealneala
J
5

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!!
Joelie answered 9/7, 2021 at 5:47 Comment(0)
S
2

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!!");
    }
}
Selsyn answered 12/6, 2020 at 3:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.