5
votes

Imagine these specifications are from an external dll. A class that implements an interface explicitly:

public interface IDebug
{
    string GetImportantInfo();
}
public class ExternalClass : IDebug
{
    public void DoSomethingImportant()
    {
        System.Diagnostics.Debug.WriteLine("Something important was done...");
    }
    string IDebug.GetImportantInfo() //Explicit implementation
    {
        DoSomethingImportant();
        return nameof(ExternalClass);
    }
}

Then this one is from internal code, where you know you need to implement the interface:

public class Debug : ExternalClass, IDebug
{
    public string GetImportantInfo()
    {
        return nameof(Debug);
    }
}

Now when I'm calling the Debug's GetImportantInfo() method from the subclass, the explicit implementation in the superclass is not called:

static void Main(string[] args)
{
    IDebug test = new Debug();
    var impInfo = test.GetImportantInfo();
    System.Diagnostics.Debug.WriteLine(impInfo); //"Debug"
}

And the only slight hint I seem to get is that I don't get a compile error when adding the IDebug interface to the Debugclass, without implementing the method:

public class Debug : ExternalClass, IDebug
{
}

Why is there no compile warning when you overwrite a superclass's implementation like this? If the base class implements it implicitly, I get a compile warning telling me to use the new keyword. But using the new keyword to overwrite an explicitly implemented method gives a compile warning:

The member 'Program.Debug.GetImportantInfo()' does not hide an inherited member. The new keyword is not required.

Is there an intended purpose for this, or is this a bug? If it's intended, what is the official reasoning?

2
Well, you shouldn't be inheriting from any class without fully understanding it in the first place. Knowing what interfaces the base class implements (directly or not) seems like a reasonable thing to expect. Considering this and knowing how C#'s interface re-implementation works should be enough to avoid the warningInBetween
Also, the warning you get when the interface is implemented implicitly in the base class has little to do with interface implementation at all; its simply good old method hiding.InBetween
You're of course free to have that opinion. But I essentially equate your advice to "just don't make mistakes". Making the assumption that everyone is infallible, is an impossible expectation to me. Being in a hurry or being forgetful are two normal use cases where this could happen. And suddenly you could spend a long time trying to debug the unexpected behavior. I don't see a difference between the need for the "good old method hiding" and this.Aske B.
@Aske.B again, because the method hiding warning is due to the fact that you have two identical methods. It has nothing to do with interfaces. When one is implemented explicitly you stop having two identical methods and there is no method hiding to begin with. You are expecting a warning in one case based upon the false belief that the warning in the other case is due to the same reason, and its not.InBetween
I'm confused by the premise of the question. Is the question premised on the idea that you're deriving from a base class and you don't know that the base class implements an interface? You are required to know that! You are required to know everything about the public surface area of a base class because that becomes the public surface area of the derived class, which you are responsible for documenting! The set of (public) interfaces implemented is part of the public surface area.Eric Lippert

2 Answers

1
votes

The problem here is that you are using a little known feature of the language: interface re-implementation:

public class Debug : ExternalClass, IDebug
{
    public string GetImportantInfo()
    {
        return nameof(Debug);
    }
}

Why are you redeclaring that Debug implements IDebug if ExternalClass already does? You are re-implementationing the interface, and becuase you are doing such thing, you get no warning; the compiler assumes you know what you are doing.

If you want the behavior you seem to want, simply don't re-implement the interface:

public class Debug : ExternalClass
{
    public string GetImportantInfo()
    {
        return nameof(Debug);
    }
}

If the base class implements it implicitly, I get a compile warning telling me to use the new keyword.

This warning has nothing to do with interface implementation. The warning is simply due to method hiding, you have two methods with the same signature; IDebug is a non factor here, you could put it out of the equation and you'd still get the same warning.

In my colleague's case, he said he had to implement both the base class and the interface because it was an event-based interface.

Well, then tell your colleague to figure out what he wants. If you reimplement the interface, then any call to DoSomething, be it through a Debug typed reference or an IDebug typed reference, should call the reimplemented behavior. Any other behavior would be unexpected and deeply bewildering.

On the other hand, if you need to keep the orignal behavior of the base class if calling DoSomething() through a IDebug typed reference then do not re-implement the interface. What other alternative are you proposing?

Does this mean that you should know about what interfaces the base class implements? Well yes, of course. I find your question about why should anyone know what interfaces any given class you are going to inherit from implements deeply worrisome to be honest.

0
votes

If you use Explicit implementation than method is not visible in your class without casting to interface. You will not be able to call

new ExternalClass().GetImportantInfo()

but you can call

((IDebug)new ExternalClass()).GetImportantInfo();

Because of Explicit implementation new keyword is not necessary. You could even add both implementations in one class:

    public class ExternalClass : IDebug
    {
        string IDebug.GetImportantInfo() //Explicit implementation
        {
            return "Explicit";
        }
        public string GetImportantInfo() 
        {
            return nameof(ExternalClass);
        }
    }

Additionally your Debug class does not have to inherit IDebug interface as it inherits ExternalClass. Resharper will show you this as redundant.