3

C# 8.0 has a new feature that lets you add a default implementation to a method on an interface. Either I'm doing something wrong or this feature doesn't work as advertised. (I'm guessing it's the former.)

I created a new .NET Core 3.1 console app with the following code:

using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            var xxx = new MyClass { MyInt = 5 };
            Console.WriteLine(xxx.GetItNow());
        }
    }

    public interface ITest
    {
        int MyInt { get; set; }
        int GetItNow() => MyInt * 2;
    }

    public class MyClass : ITest
    {
        public int MyInt { get; set; }
    }
}

The Console.WriteLine(xxx.GetItNow())); statement doesn't compile because

Myclass does not contain a definition for 'GetItNow()'...

So, the compiler is happy with the fact that MyClass doesn't explicitly reference GetItNow() (it doesn't complain the MyClass doesn't implement the interface). But it doesn't see the default interface member as a public method of the class that implements the interface.

Am I missing something, or is something broken?

Julian
  • 33,915
  • 22
  • 119
  • 174
Bob.at.Indigo.Health
  • 11,023
  • 13
  • 64
  • 111
  • 3
    `Console.WriteLine((xxx as ITest).GetItNow()));` default *interface* method belongs to *interface* only (not to *class* which implements it); that's why cast (`xxx as ITest` or alike) is mandatory here. `ITest xxx = new MyClass { MyInt = 5 };` is an alternative (now `xxx` is declared as *interface*) – Dmitry Bychenko May 10 '20 at 19:38
  • 1
    Oh! Thank you! Yes, the "documentation" (actually, an overly complex tutorial) contains a mention of the fact that, in order to access the default interface member, you must cast the object reference to the interface. So this compiles: `Console.WriteLine(((ITest)xxx).GetItNow());` – Bob.at.Indigo.Health May 10 '20 at 19:47

2 Answers2

8

Well, interface default method belongs to the interface not to a class which implements it; so you have two possibilities:

Cast:

  Console.WriteLine(((ITest)xxx).GetItNow()));

Declaration change (preferable; MyClass is implementation detail, often it's a dependency; ITest - contract is the only thing which matter):

  ITest xxx = new MyClass { MyInt = 5 };
  // xxx is ITest, so xxx.GetItNow() is legal now 
  Console.WriteLine(xxx.GetItNow());
Dmitry Bychenko
  • 180,369
  • 20
  • 160
  • 215
0

https://stackoverflow.com/a/61717913/779967 is excellent and clear answer, I was trying to do the same with this new feature but got stopped by the compiler. Now I know why.

If your intention is to mark some class with an interface and get some functionality attached to it. I’ve been using extension methods on the interface itself to achieve this effect. The only downside is that only functions are supported at this time not properties.

public interface ITest
{
    int MyInt { get; set; }
}

public static class ITestExtensions
{
    public static int GetItNow(this ITest self)
    {
        return self.MyInt * 2;
    }
}
Jorge
  • 69
  • 3