1

I have a .Net 6.0 application in Visual Studio 2019. I'm trying to get default interface implementations working. For some reason, it doesn't seem to be recognizing the default implementations in the class definition.

Here is a sample code snippet:

public interface IFooBar
{
    protected bool BoolProperty { get; set; }
    protected Guid StringProperty { get; set; }
    
    protected void SampleMethod1(string param)
    {
    }
    
    protected void SampleMethod2()
    {
    }
}

public class FooBase
{
}

public class Foo : FooBase, IFooBar
{

    protected bool IFooBar.BoolProperty { get; set; }
    protected Guid IFooBar.StringProperty { get; set; }
    
    protected SomeMethod()
    {
        SampleMethod1("Test String");
    }
}

Here is a snippet from my Visual Studio 2019 project file:

<PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
    <LangVersion>preview</LangVersion>
</PropertyGroup>

Here is the error message I'm seeing.

Error CS0103 The name 'SampleMethod1' does not exist in the current context

I've got two questions/issues:

  1. Why did the compiler require me to define my interface properties as such in my concrete class: protected bool IFooBar.BoolProperty { get; set; } protected Guid IFooBar.StringProperty { get; set; }

  2. Why is the default method implementation not recognized in my concrete class?

Panagiotis Kanavos
  • 104,344
  • 11
  • 159
  • 196
JohnB
  • 3,351
  • 5
  • 39
  • 68
  • Is it really legal in C# 10 to add the `protected` keywords in the class when you also have explicit interface implementations? And `protected SomeMethod()` without a return type or `void`? – Lasse V. Karlsen Oct 28 '21 at 21:23
  • Protected interface members seem to be entirely pointless, as far anyone's been able to work out, it seems. They can be explicitly implemented, but there's no way to call them on the base class. The [spec proposal](https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/default-interface-methods) leaves this as an "Open Issue", and [this blog post](https://jeremybytes.blogspot.com/2019/11/c-8-interfaces-public-private-and.html) gave up trying to understand the point – canton7 Oct 28 '21 at 21:30
  • Note that default interface implementations are not available as members of the implementing type. There will be no method on the class type named `SampleMethod1`. You could cast the object instance to the interface to access it, but the `protected` keyword explicitly removes the method from the viable overload list so it is unavailable. If it were public in the interface, you could write this in your class: `((IFooBar)this).SampleMethod1("Test String");`. – Lasse V. Karlsen Oct 28 '21 at 21:30
  • @canton7 `protected` interface methods seems to be a way to add, as you say, methods available to default implementations in interfaces that derive from the one declaring the protected method. It seems to me that the C# compiler design team hasn't seen the rule that "just because you can't doesn't mean you should". It seems to me to be an example of adding features just because they can, but not really having a clear usecase for why they should. I guess the "every feature starts with minus 100 points" rule have gone out the window. – Lasse V. Karlsen Oct 28 '21 at 21:38
  • @Servy I don't think that's a dup? That shows how to access `public` DIM methods, but the same technique doesn't work for `protected`. See e.g. [here](https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEA3GUCWAZgJ4A+AAgEwCMAsAFAPkDMABPgHYZ6ECGYMVgEkAYhAgAhXlAYBvBq0WsADlAjcw3ACatg4gDasAGq1msA5jAwBuVgGcrtgL4MXjei1ZVWYiKxDCvlIy9PL0SroGgeLBAHQmZpY29o6sbhEKSp7kKKwAyhAAtjAAslYAFhBaABQAlJmKYRER5NQAnNVGtdYNza0d1dWiMdK1GOX4drXx3b1ubkA=) – canton7 Oct 28 '21 at 21:39
  • It seems that they're usable from derived interfaces, [see here](https://gitter.im/dotnet/csharplang?at=617b1928ee6c260cf747bf0e) – canton7 Oct 28 '21 at 21:43
  • @canton7 Yes, I've verified that with two levels of interfaces, the protected default implementation methods of the first level is accessible in default implementation methods in the second. They're not available in the class, however. – Lasse V. Karlsen Oct 28 '21 at 21:44
  • Yeah, that's an odd state of affairs -- I can't imagine when that would ever be useful, and there doesn't seem to be any documentation for that, and the compiler doesn't guide you towards it – canton7 Oct 28 '21 at 21:44
  • @canton7 It shows them the only way to ever call a default interface implementation. If they also want to know why members can't be accessed outside of where there accessibility allows, I guess you could find a duplicate for the too, although the error message for it is rather self explanatory. – Servy Oct 28 '21 at 21:44
  • https://stackoverflow.com/questions/62832992/when-should-we-use-default-interface-method-in-c – Hans Passant Oct 28 '21 at 21:45
  • @Servy Right but as we've discovered, you *can't* call protected DIM members. There is no syntax to do so from an implementing *class*. You can do so from an implementing *interface* with no special syntax however. Your dup was all about casting the concrete type to the interface type, but that syntax has zero relevance when calling protected DIM members – canton7 Oct 28 '21 at 21:45
  • @canton7 Right. If they set too restrictive of an accessibility for their needs they'll need to change it. Just like literally every other time someone tries to access an inaccessible member and gets the exact same error. – Servy Oct 28 '21 at 21:46
  • I think the error `Cannot access protected member 'IFooBar.X' via a qualifier of type 'IFooBar'; the qualifier must be of type 'Foo' (or derived from it)` says something a bit different, heh. – canton7 Oct 28 '21 at 21:47
  • @LasseV.Karlsen on -100 points - feature may be "*remove* protected from list of access modifiers on interface methods" rather than "*add* protected to the list..." (I have no idea if it is the case, but it is often quite hard to know whether feature was explictly added or rather not remove when added along with other features) – Alexei Levenkov Oct 28 '21 at 22:05
  • 2
    @AlexeiLevenkov Good point, though in this case I feel like the entire -100 point thing on my part relates to the addition of access modifiers for members of interfaces. They should've left it as just `void SampleMethod1()`, always public through the interface. But, that's just my opinion. – Lasse V. Karlsen Oct 28 '21 at 22:06

1 Answers1

1

It turns out that protected Default Interface Method members must be implemented explicitly by the implementing class, but can only be accessed from derived interfaces.

For example:

public interface IBase
{
    protected string StringProperty { get; set; }
    
    protected void BaseMethod(string param) => Console.WriteLine($"IBase.BaseMethod: {param}");
}

public interface IDerived : IBase
{
    public void DerivedMethod()
    {
        // SampleMethod1, SampleMethod2 and StringProperty are accessible.
        BaseMethod(StringProperty);
    }
}

public class Foo : IDerived
{
    // Protected DIM properties must be explicitly implemented.
    // They can be initialized, interestingly, but are otherwise inaccessible to Foo.
    string IBase.StringProperty { get; set; } = "StringProperty";
    
    public void Test()
    {
        // Public DIM members are available via cast
        ((IDerived)this).DerivedMethod();
    }

    // Protected DIM members can be overridden.
    // There doesn't seem to be a way to access the base method in the override.
    void IBase.BaseMethod(string param) => Console.WriteLine($"Foo.BaseMethod: {param}");
}

SharpLab.

canton7
  • 32,489
  • 3
  • 56
  • 67