Search code examples
c#overridingdefault-interface-member

Making sure a C# method matches the signature of an interface default implementation


Consider the following C# example:

using System;

interface IExample
{
   public int Foo() => 42;
}

class ExampleImplementation : IExample
{
   public int Foo() => 37;
}

class Program
{
   static void Main(string[] args)
   {
       IExample example = new ExampleImplementation();
       Console.WriteLine($"{example.Foo()}");
   }
}

The code will output 37 as expected.

However, if I change the method signature in the interface, but NOT that of the implementation, like this:

interface IExample
{
   int Foo(int additional_argument) => additional_argument + 42;
}

class ExampleImplementation : IExample
{
   int Foo() => 37;
}

class Program
{
   static void Main(string[] args)
   {
       IExample example = new ExampleImplementation();
       Console.WriteLine($"{example.Foo(0)}");
   }
}

The program will output 42 instead.

Now this is undesired behavior, because the method Foo in ExampleImplementation is written to implement (and override) the default implementation, but in the new code it fails to do so.

There are ways to mitigate this, such as using an IDE that allows you to change the signature of methods along the chain. However, I wish to know whether is anything at the language level that can prevent this.

As a side note, in C++, the override keyword is used for exactly this purpose: to make sure a member function is actually overriding something from the base. If I were using an abstract class, then I can do something similar, but I want to know if there is something similar to default interface implementations.


Solution

  • Explicit interface implementations in C# are required to match the signatures of the method they are implementing, so you could do:

    class ExampleImplementation : IExample
    {
       int IExample.Foo() => 37;
       
       public int Foo() => ((IExample)this).Foo();
    }
    

    The public method is added so that you can still call Foo on something of type ExampleImplementation.

    Alternatively, you can do it "the other way round":

    class ExampleImplementation : IExample
    {
       int IExample.Foo() => Foo();
       
       public int Foo() => 37;
    }