(Thanks to Don Largen for posing this question to me)
C# (.NET in general) supports polymorphism through the use of inheritance and the use of the virtual/abstract mechanisms for overriding methods in subclasses (btw, if there are any non-programmers who read my blog, you may want to just skip this entry). At first glance, C# appears to share this concept with both C++ and Java.
Appearances can be deceiving.
Suppose we declare the following interface declaration (in C#):
public interface IFoo { void doSomething(); }
And suppose further that we declare a class A that implements this interface:
public class A : IFoo { public void doSomething() { Console.WriteLine("In A.doSomething()"); } }
We can then use an A like an IFoo:
IFoo foo = new A(); foo.doSomething(); //outputs: In A.doSomething()
Basic stuff. However, for this to be possible, the .NET runtime has to be able to find A.doSomething() when it references IFoo.doSomething(). This is achieved by manipulating the address space of the in-memory representation of the object reference. This varies by language, but generally involves using one or more vtables which are (simply put) a list of address locations for each method available on the object, mapped to the implementation on each type supported by the object. (Again simply put) Regular methods have their addresses compiled into the code, while virtual methods have an entry in the vtable compiled in, this pointer is then changed -- at runtime -- to point to the actual method that should be called (depending on the specific reference and the polymorphism rules of the language). Once marked virtual, a method signature remains virtual throughout the type hierarchy (i.e. calls to that method signature will always go through the vtable lookup regardless where in the type hierarchy the reference points).
Since interfaces are also types, interface method definitions are virtual. This, suggests that they can be overridden. In C# (like C++, and unlike Java) virtual method declarations normally require that the keyword 'virtual' appear in their signatures (so that the compiler knows to add the vtable entry instead of the actual address of the method), but with interfaces that requirement is waived since they obviously require classes that inherit their signatures (strictly speaking interfaces are not base types of implementing classes, for more see Don Box's Essential .NET Volume 1 page 70) to provide an implementation. Also in C# (unlike C++ or Java) another keyword 'override' is required by the subclass in order to actually replace the vtable entry for the method signature. Finally (ahem), C# sports the 'sealed' keyword which (like 'final' in Java) doesn't make the method non-virtual (i.e. can't be inlined by the compiler) but does make it so that subclasses can't override them.
Since virtual method signatures remain virtual in subclasses, and interface methods are virtual then subclasses of classes that implement interfaces should be able to override the interface method implementations. One would think that we should be able to do this:
public class B : A { public override void doSomething() { Console.WriteLine("In B.doSomething()"); } }
But this doesn't work in C#, the compiler chokes on it:
B.doSomething()' : cannot override inherited member 'A.doSomething()' because it is not marked virtual, abstract, or override
Well, if that's not it, perhaps we can simply imply it like we did for A. i.e. What happens if we leave 'override' out of the method declaration, like this:
public class B : A { public void doSomething() { Console.WriteLine("In B.doSomething()"); } }
This compiles, but that isn't what we want either -- as the following warning tells us:
The keyword new is required on 'B.doSomething()' because it hides inherited member 'A.doSomething()'
Hides inherited member? What does that mean? It means that C# (like C++, but unlike Java) allows subclasses to have methods with the same signature as those in base classes -- and remain not virtual i.e. the implementation is not included in the vtable lookup. In a sense, B has two doSomething() methods -- the one it inherits from A, and this new one (which is why C# requires the 'new' keyword). Thus, using B as an IFoo doesn't behave the way we expect it to:
IFoo foo = new B(); foo.doSomething(); //still outputs: In A.doSomething() - not what wanted!
So now what? One option is to include 'virtual' in the signature of A.doSomething(), but what if we don't have access to A's code? Well, it turns out that if we explicitly tell C# that B implements IFoo then everything works:
public class B : A, IFoo { void IFoo.doSomething() { Console.WriteLine("In B.doSomething()"); } } IFoo foo = new B(); foo.doSomething(); //outputs: In B.doSomething()
But there's a catch. Note the syntax differences in B: We have to explicitly say we are implementing IFoo.doSomething -- and leave off the public keyword. We can't simply implement the method like we did in A (unless we want to go through the warnings and errors like before). What gives?
For starters, prepending 'IFoo.' on the method declaration does more than disambiguate what method is being implemented -- it also changes its visibility. In a sense this method declaration is private on B! (hence the need to remove the public keyword). That is, B's definition of doSomething() is not visible on B instances (or A's) only on explicit IFoo references:
B b = new B(); A a = b; b.doSomething(); //outputs: In A.doSomething() !! a.doSomething(); //so does this
Confused? So was I until I looked it up in Essential .NET. In addition to suggesting the above implementation, provides an explanation for the seeming inconsistency:
Essential .NET Volume 1 page 169:
Yup, you read that right: implementing an interface method implicitly seals the method (as well as overriding it). So, unless you tell it otherwise, the first implementation of an interface ends the override chain in C#.
Now, if you are like my friend Don Largen (and myself), you may not like this kind of behind-the-scenes magic (Don and I both learned OO programming in C++ so that might explain it), but there is some logic to it -- if you follow the basic language design philosophy (and not narrower concept of virtualization as we've done here):
C# (.NET) adheres (kind of) to a design philosophy that class extension should be an explicit design decision of the base class designer i.e. the desire to have one's classes extended should be explicitly indicated. Conversely, C++ doesn't provide class designers with the ability to finalize, and while Java does, one must be explicit -- all methods are virtual by default. This is a topic of much controversy, but I think it comes down to the difference between enabling and directing design preferences -- and I prefer to use (and design) enabling technologies.
However, since I believe that arguing about language design choices is akin to tilting at windmills, my preference these days is to mark the pitfalls, understand why they are there, and then move on -- hopefully this entry will help you do the same. For more details see the relevant sections of Essential .NET (you do have a copy of it don't you?).