The architects of the .NET framework made a specific decision not to use multiple inheritance. There are arguments on “they should have” and “they did the right thing”, and there are some work-arounds where you can kind of get the feeling of multiple inheritance. But I’m not going to specifically focus on that right now. Today I’m talking about interfaces, and a problem that I ran into converting some code from .NET 1.1 to 2.0.
When you start talking about the need for multiple inheritance, the answer that inevitably comes up is interfaces. That’s all well and good, but it still leaves you with having to implement all of those interfaces in each class where they are used. Not quite as fancy as just straight inheritance. The ability to implement these multiple interfaces still leaves you with one of the problems of multiple inheritance as well. I’m talking about method overloading.
Let’s say in your code you have two interfaces, Ione and Itwo. Let’s also say that you have a bunch of classes that implement Ione and Itwo.
public interface Ione{}
public interface Itwo{}
public class UsesInterfaceOne : Ione {}
public class UsesInterfaceTwo : Itwo {}
This allows you two write code like this
public class Runner
{
public static string Run(Ione one)
{
return "Ran ONE";
}
public static string Run(Itwo two)
{
return "Ran TWO";
}
}
But suppose someone comes along and decides to introduce a class that looks something along the lines of this (can you guess what’s coming next?)
public class UsesBothInterfaces : Ione, Itwo {}
Oh damn. What happens when you run Runner.Run() ?
Well now…..that all depends. How are you calling the method and what framework are you using? If you are just running the code directly like so|
UsesBothInterfaces both = new UsesBothInterfaces();
Runner.Run(both);
Then that code won’t even compile. It tells you that the call is ambiguous.
But what if you think that you’re being slick, and you have an XML file that drives your navigation, and you are calling your methods via reflection and Type.InvokeMember(). Say your code looks something like this
Type.GetType("MultipleInterfaces.Runner").InvokeMember("Run", BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy | BindingFlags.InvokeMethod, null, null, new object[] {o})
Yeah. So in the .NET framework 1.1, that runs fine. Well, sort of fine. What it seems to be doing is calling the last method that is listed in your class. Now I haven’t analyzed it further than stepping through the code, and trying to flip the methods around to see which one is being called. So it may be doing something a bit more fancy than calling the last one. But that seems to be what my tests are showing.
NOW, take your code and port everything over to .NET 2.0. Either convert it in the IDE or just go into IIS Manager and switch it over to 2.0. Note: depending on what version of IIS you are using, you’ll probably have to reset IIS or move your app to a new app pool. Now run that bit of reflection code and you should be seeing an exception. Specifically it is throwing a System.Reflection.AmbiguousMatchException.
So they tightened up reflection a little bit. I would say for the better. Even though it looks like there is now a possible breaking change during runtime in your app. But if you are using reflection to poke around and call methods, you should probably be writing your code to be able to handle these kinds of exceptions and error out or continue on gracefully.
If you want to the sample code that I created when testing this, you can get it here: MultipleInheritance.zip