views:

85

answers:

1

In the following code, I'm implementing an interface, and then deriving from that class and implementing the same interface. When I dispose an instance of the derived class, it only calls the derived implementation.

Why wouldn't the C# compiler warn about this? It seems dangerous for a couple reasons. I could implement an interface that the base had implemented, which would give me behavior I wasn't expecting (such as the base no longer disposing). Or if I later decide to have a base implement a new interface, I have to scan all my code to find any derivatives that could be implementing it already.

For duplicate members in derived classes, the compiler requires us to use 'override' or 'new'.

Why are interfaces different?

class CA : IDisposable
{
    void IDisposable.Dispose() { Debug.WriteLine("CA.Dispose"); }
}

class CB : CA, IDisposable
{
    void IDisposable.Dispose() { Debug.WriteLine("CB.Dispose"); }
}

class Program
{
    static void Main(string[] args)
    {
        using (new CB()) { }
    }
}

// output: CB.Dispose
+5  A: 

It's only doing that because you're using explicit interface implementation. For example, this doesn't compile:

using System;
using System.Diagnostics;

class CA : IDisposable
{
    public void Dispose()
    {
        Debug.WriteLine("CA.Dispose");
    }
}

class CB : CA, IDisposable
{
    public void Dispose()
    {
        Debug.WriteLine("CB.Dispose");
    }
}

So yes, you need to be careful with explicit interface implementation. That's always the case, as it's a little odd in terms of the type system anyway.

It's not ideal, but explicit interface implementation is there partly to get round awkward situations where you want to be able to reimplement an interface.

To be honest, if you decide at a late stage that a base class should start to implement IDisposable, you're in a world of hurt anyway - because you'll have to inspect every place you're using that class in order to tell whether or not you need a using statement.

Jon Skeet
I understand the mechanics involved, but was asking more about motivations. "You need to be careful" is my main issue. Why did MS choose to put the burden of checking for this on the developer? With members, they explicitly look for this and warn if you're missing the 'new' or 'override'. Why wouldn't I be required to use "class CB : CA, new IDisposable"? I realize we can't read Anders's mind, but is this required awkwardness a hint that we should not use explicit interfaces at all? I find them immensely useful specifically because they add extra compile time type safety. What does Java do?
Scott Bilas
@Scott: I think your point is that implicit interface re-implementation without "new" on it leads to a form of the brittle base class problem; an unexpected change in the base class can lead to derived classes having unexpectedly bad behaviour. Warning if "new" is missing on methods is designed to mitigate that failure for virtual methods. By analogy, we could require the same on interface re-implementations. I have not given this much thought, but yes, at first glance this does seem to be an area where the initial design could have been better. It would be a nasty breaking change now though.
Eric Lippert
That's the answer I was looking for, thank you.
Scott Bilas
@Scott, what do you mean when you say that explicit interfaces "add extra compile time type safety"?
Jeff Sternal
@Jeff - Say you explicitly implement an interface. If you remove a member from the interface, every single implementation will break and needs revisiting. Whereas if it's implicit, then those implementations hang around as ordinary publics and become stale code.
Scott Bilas