Skip to content

C# override and new inheritance directives

Sunday, 25 April 2004  |  richard dale

I've been learning about C# over the past month or two, and mostly I haven't found anything too much to get annoyed about. Maybe I'd prefer it if the method names started with a lower case letter. Some of the classes such as 'ContextBoundObject' have unwieldy confusing names, which makes reading about them harder than it should be. And worse, the C# books don't seem to have any jokes, and when you do something to save time it's in 'order to add business value', hmm exciting. They spell 'Marshaling' instead of 'Marshalling', which looks a bit wrong to me.




But one feature which just 'smells bad' is the 'new' inheritance directive. Here's some example code using the 'override' and 'new' from 'Programming .NET Components' by Juval Lowy:

#define TRACE

using System;
using System.Diagnostics;
using System.IO;

public class BaseClass {
	public virtual void TraceSelf() {
		Trace.WriteLine("BaseClass");
	}
}

public class SubClass : BaseClass {
	public override void TraceSelf() {
		Trace.WriteLine("SubClass");
	}
}

public class TopLevel {
	static void Main(string[] args) {
		TextWriter tw = Console.Out;
		Trace.Listeners.Add(new TextWriterTraceListener(tw));
		
		BaseClass obj = new SubClass();
		obj.TraceSelf(); // Outputs "Subclass"
	}
}	

If you want to provide the base class behaviour instead, use the new directive, with or without virtual at the base class

public class BaseClass {
	public virtual void TraceSelf() {
		Trace.WriteLine("BaseClass");
	}
}

public class SubClass : BaseClass {
	public new void TraceSelf() {
		Trace.WriteLine("SubClass");
	}
}

public class TopLevel {
	static void Main(string[] args) {
		TextWriter tw = Console.Out;
		Trace.Listeners.Add(new TextWriterTraceListener(tw));
		
		BaseClass obj = new SubClass();
		obj.TraceSelf(); // Outputs "BaseClass"
		
		SubClass obj1 = new SubClass();
		obj.TraceSelf(); // Outputs "SubClass"
	}
}	



See how in the second example the instance of 'SubClass' behaves differently according to whether you define the variable holding the reference as a 'BaseClass' or a 'SubClass'. Clearly this feature was added because they felt they had a problem which needed solving. To me it just looks like an accident waiting to happen.



I had a lot of trouble trying to remember the difference between 'new' and 'override' because it just doesn't fit with my mental model of OO as objects responding to messages. When I learn a new language, everything is 'Objective-C with a different syntax' as a starting point, because that's the language I've spent most time with. The 'new' keyword could never be implemented in Objective-C, and hence my problems learning about it.



I think the real problem is that they are missing 'Design by Contract', where pre-conditions, invariants and post-conditions could be specified in the BaseClass.TraceSelf() method. Then if the re-implementation of TraceSelf() in SubClass violated the contract, the assertions would fail.



The 'new' keyword is really a hack, a label to say "Please Ignore Design By Contract". All the checking is based on some human being reading and unstanding fine details of the language syntax, and not on the semanatics of what is actually going on inside the methods.




When I compile the Kimono Qt classes, I get about 150 warnings which are mostly about incorrect use of 'new':
QIconSet.cs(141) warning CS0109: The member `Qt.QIconSet.Dispose' does not hide an inherited member.  
The keyword new is not required
..
QFile.cs(58) warning CS0114: `Qt.QFile.Open' hides inherited member `QIODevice.Open'.  
To make the current member override that implementation, add the override keyword, otherwise use the new keyword

I just find these messages a bit tedious, and not especially helpful. Oh well. Now I've got how 'new' works finally fixed in my head, the next step is just flog through the reasons for them all and try and improve the kalyptus code generation..