views:

233

answers:

2

While attempting to override the explicit interface implementation of the ICollection<T>.IsReadOnly property from the Collection<T> class, I came across some documents stating that explicit interface member implementations cannot be overridden because they cannot have modifiers such as virtual or abstract. On MSDN they even go as far as specifying how to make an explicit interface member implementation available for inheritance by creating another abstract or virtual member which is called by the explicit interface member implementation. No problems so far.

But then I wonder: Why is it possible in C# to override any explicitly implemented interface member just by specifying the interface explicitly?

For example, suppose I have a simple interface like this, with a property and method:

public interface IMyInterface
{
    bool AlwaysFalse { get; }
    bool IsTrue(bool value);
}

And a class A which implements the interface explicitly, and has a method Test() which calls its own interface member implementation.

public class A : IMyInterface
{
    bool IMyInterface.AlwaysFalse
    { get { return false; } }

    bool IMyInterface.IsTrue(bool value)
    { return value; }

    public bool Test()
    { return ((IMyInterface)this).AlwaysFalse; }
}

As you can see, none of the four members are virtual or abstract, so when I define a class B like this:

public class B : A
{
    public bool AlwaysFalse
    { get { return true; } }

    public bool IsTrue(bool value)
    { return !value; }
}

Then you'd expect an instance of B cast to A to behave like A. And it does:

A a = new A();
Console.WriteLine(((IMyInterface)a).AlwaysFalse);    // False
Console.WriteLine(((IMyInterface)a).IsTrue(false));  // False
Console.WriteLine(a.Test());                         // False
A b = new B();
Console.WriteLine(((IMyInterface)b).AlwaysFalse);    // False
Console.WriteLine(((IMyInterface)b).IsTrue(false));  // False
Console.WriteLine(b.Test());                         // False

Now comes the catch. Create a class C which is an exact copy of B except for one thing in the class declaration:

public class C : A, IMyInterface
{ /* ... same as B ... */ }

Now an instance of C, when cast to A, doesn't behave like A but like C:

A c = new C();
Console.WriteLine(((IMyInterface)c).AlwaysFalse);    // True
Console.WriteLine(((IMyInterface)c).IsTrue(false));  // True
Console.WriteLine(c.Test());                         // True

Even the Test() method now calls the overridden method in C! Why is this?

A: 

When you implement an interface explicitly , as in class c and you create the object of that class and cast it to the implemented interface then that instance will work on the implemented interface.

And it sounds logical also because If a class is implementing an interface so that interface and work.

saurabh
@saurabh: fyi, it's "you", not "u"
John Saunders
+3  A: 

This has nothing to do with explicit interface implementation; it's simply a consequence of the general rules of inheritance and interface mapping: you would see exactly the same results if type A provided an implicit, rather than explicit, implementation of IMyInterface.

  • Type B inherits from type A. Nothing is overridden.
    B provides its own AlwaysFalse and IsTrue members but they don't implement IMyInterface; The implementation of IMyInterface is provided by the members inherited from A: when an instance of type B is cast to IMyInterface then it behaves in exactly the same way as an instance of type A because A is providing the members that implement the interface.
  • Type C inherits from type A. Again, nothing is overridden.
    C provides its own AlwaysFalse and IsTrue members but this time those members do implement IMyInterface: when an instance of type C is cast to IMyInterface then the members of C provide the interface implementation rather than those of A.

Because type A implements IMyInterface explicitly the compiler doesn't warn that the members of B and C are hiding the members of A; In effect those members of A were already hidden due to the explicit interface implementation.

If you changed type A to implement IMyInterface implicitly rather than explicitly then the compiler would warn that the members of B and C are hiding, not overriding, the members of A and that you should ideally use the new modifier when declaring those members in B and C.

Here are some relevant bits from the language specification. (Sections 20.4.2 and 20.4.4 in the ECMA-334 spec; sections 13.4.4 and 13.4.6 in the Microsoft C#4 spec.)

20.4.2 Interface mapping

The implementation of a particular interface member I.M, where I is the interface in which the member M is declared, is determined by examining each class or struct S, starting with C and repeating for each successive base class of C, until a match is located.

20.4.4 Interface re-implementation

A class that inherits an interface implementation is permitted to re-implement the interface by including it in the base class list. A re-implementation of an interface follows exactly the same interface mapping rules as an initial implementation of an interface. Thus, the inherited interface mapping has no effect whatsoever on the interface mapping established for the re-implementation of the interface.

LukeH
So you say that it is logical and expected that anyone can provide a new implementation for any explicit interface member, and above all, that the original class will call that new member (the one hiding the original implementation) without any overriding, virtual or abstract methods whatsoever?
Virtlink
It's precisely because you're *not* overriding that you're seeing this behaviour: For example, type `B` provides a *new* implementation of `IsTrue` that *does not* override the `IsTrue` method from type `A`. Type `B` only inherits its implementation of `IMyInterface` from `A`. It's the `A.IsTrue` method that implements `IMyInterface` so when you cast an instance of `B` to `IMyInterface` it's the `A.IsTrue` method that's used. [...]
LukeH
Type `C` also provides a a *new* implementation of `IsTrue` that *does not* override the `IsTrue` method from type `A`. But type `C` implements `IMyInterface` directly. When you cast an instance of `C` to `IMyInterface` it's the `C.IsTrue` method that's used, in accordance with the rules from the language specification.
LukeH
I agree that it's somewhat counter-intuitive, but it's just a consequence of the rules of the language! If the `A.Test` method returned `((C)this).AlwaysFalse` then you'd rightly expect it to start looking in `C` for a matching member, followed by its ancestors from newest to oldest (assuming that `this` is actually a `C`). Similarly when `A.Test` returns `((IMyInterface)this).AlwaysFalse` then it looks for the "newest" member that implements `IMyInterface.AlwaysFalse` (and that's the one provided by `C`, again assuming that `this` is actually a `C`).
LukeH
Of course I see the logic of hiding, and casting to the interface calling the new member. But in this situation specifically, it is strange that there can be two (or more) implementations of the same interface in a class. I think it is a language error to allow implementing the same interface more than once on a class, which leads to this kind of situations.
Virtlink
@Virtlink: It is a bit strange, but it's all spelled out in the language spec. (I've edited my answer to include another relevant quote from the spec.)
LukeH
I see. Since you've provided an explanation as to why it works this way, and shows that it is even explicitly allowed, I accept your answer now.
Virtlink