user1777224
user1777224

Reputation: 878

Why do I need to cast 'this' to an interface with a default implementation in C# 8.0 when I call it in the class derived form that interface?

I have this Simple Console program in .NET Core 3.1 with C# 8:

using System;

namespace ConsoleApp34
{

    public interface ITest
    {
        public void test()
        {
            Console.WriteLine("Bye World!");

        }
    }

    public class Test : ITest
    {
        public void CallDefault()
        {
            ((ITest)(this)).test();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello World!");
            var t = new Test();
            t.CallDefault();

        }
    }
}

I don't understand why the cast is necessary in the line ((ITest)(this)).test();

Test is directly derived from ITest, so, by definition, 'this' IS ITest

Thank you.

Upvotes: 8

Views: 1735

Answers (3)

Johnathan Barclay
Johnathan Barclay

Reputation: 20353

Default interface implementations work similarly to explicit implementations: they can only be called through the interface type, not the implementing type.

To understand why this is so, imagine Test implemented 2 interfaces with the same method signature; which would be used if there was no cast?

public interface ITest2
{
    public void test()
    {
        Console.WriteLine("Hello World!");
    }
}

public class Test : ITest, ITest2
{
    public void CallDefault()
    {
        test(); // Do we use ITest.test() or ITest2.test()?
    }
}

It is true that the above syntax could be allowed, and instead generate a compilation error in the case of multiple interfaces, however, this contradicts with the primary motivation behind the introduction of default interface methods:

Default interface methods enable an API author to add methods to an interface in future versions without breaking source or binary compatibility with existing implementations of that interface.

If Test already implemented ITest & ITest2, then test was added to ITest2 at a later date, this would constitute a breaking change.

Upvotes: 12

Charles
Charles

Reputation: 3061

It's funny, if you create an extension method then no more cast is needed.

Just did the following in my project:

public static class IXpObjectExtensions
{
    public static Mode GetMode(this IXpObject obj) => obj.Mode;
}

Mode is defined with a default implementation in IXpObject in my case.
I can now use obj.GetMode() without casting the object to the interface.

Upvotes: 3

Rudey
Rudey

Reputation: 4975

This behavior is documented here.

Beginning with C# 8.0, you can define an implementation for members declared in an interface. If a class inherits a method implementation from an interface, that method is only accessible through a reference of the interface type. The inherited member doesn't appear as part of the public interface. The following sample defines a default implementation for an interface method:

public interface IControl
{
    void Paint() => Console.WriteLine("Default Paint method");
}
public class SampleClass : IControl
{
    // Paint() is inherited from IControl.
}

The following sample invokes the default implementation:

var sample = new SampleClass();
//sample.Paint();// "Paint" isn't accessible.
var control = sample as IControl;
control.Paint();

Any class that implements the IControl interface can override the default Paint method, either as a public method, or as an explicit interface implementation.

Upvotes: 3

Related Questions