Architecture + Strategy

Musings from David Chou - Architect, Microsoft

.NET and Multiple Inheritance

.NET and Multiple Inheritance

  • Comments 6

Occasionally I get questions on why does .NET not support multiple inheritance. It is actually a pretty interesting question to contemplate with, though I usually start the conversation by asking: "what issue requires multiple inheritance to solve?".

More often than not, the question surfaces when people are trying to "do the right thing" by correctly refactoring code in an object-oriented manner, and facilitate code reuse by using inheritance, but encounter challenges when trying to reuse methods and code behaviors defined in separate places of the class hierarchy. Thus the most "natural" question was, if I can just inherit the code from these classes...

Many decisions in language design, just like software projects, are balancing acts between various trade-offs. There are many very interesting conversations happening in the community, such as the debate on generics and closures on the Java side (for example: How Does Language Impact Framework Design? and Will Closures Make Java Less Verbose? and James Gosling's Closures). Interesting to see how much thought goes into each seemingly small decision on adding specific language features, or not adding them.

There were many factors that influenced the .NET team to favor not implementing multiple inheritance. A few of the more prominent ones include:

  • .NET was designed to support multiple languages, but not all languages can effectively support multiple inheritance. Or technically they could, but the complexities added in language semantics would make some of those languages more difficult to use (and less similar to their roots, like VB, and for backward compatibility reasons) and not worth the trade-off of being able to reuse code in the manner of multiple inheritance
  • It would also make cross-language library interoperability (via CLS compliance) less of a reality than it is today, which is one of the most compelling features of .NET. There are over 50 languages supported on .NET in over 70 implementations today
  • The most visible factor is language semantics complexity. In C++ we needed to add explicit language features in order to address ambiguities (such as the classic diamond problem) caused by multiple inheritance, such as the "virtual" keyword to support virtual inheritance to help the compiler resolve inheritance paths (and we had to use it correctly too)
  • As we know code is written 20% of the time, but read 80% of the time. Thus advocates on simplicity side prefer not to add language features for the sake of keeping semantics simple. In comparison C# code is significantly simpler to read than C++ code, and arguably easier to write

Now Java doesn't support multiple inheritance as well, though probably for a different set of reasons. Thus it is not a case of simple oversight in design or lack of maturity, as it was a careful and deliberate decision to not support multiple inheritance in the .NET and Java platforms.

So what's the solution? Often people are directed to using interfaces, but interfaces don’t lend themselves very well to meet the requirements of reusing code and implementing separation of concern; as interfaces are really intended to support polymorphism and loosely-coupled/contractual design. But other than trying to tie behaviors into object inheritance hierarchies, there are many alternative approaches that can be evaluated to meet those requirements. For example, adopting relevant design patterns like Visitor, frameworks like MVC, delegates, mixins (interfaces combined with AOP), etc.

Bottom line is, there are considerably elegant alternatives to inheriting/deriving behavior in class hierarchies, when trying to facilitate code reuse with proper re-factoring. Plus trade-offs in code reuse vs. the costs incurred to manage the reuse is another full topic in itself. In some cases it may have been simpler to have multiple inheritance and access to sealed classes, but the trade-offs may have been greater costs in other areas.

  • PingBack from http://microsoftnews.askpcdoc.com/?p=3133

  • Ancora su .NET e l'ereditariet

  • > Often people are directed to using interfaces, but interfaces don’t lend themselves very well to meet the requirements of reusing code and implementing separation of concern; as interfaces are really intended to support polymorphism and loosely-coupled/contractual design.

    I disagree. Using interfaces in conjunction with wrapping can cover 9% out of the remaining 10% that isn't covered by interfaces alone. You can even do "virtual base classes" that way, though rather more explicitly than in C++. The only real problem, aside from the perfomance penalties, is the tediousness of writing all the wrapping/delegating code; but add syntactic sugar to do that (delegate calls to methods of a particular implemented interface to a wrapped object) with a single line of code, and I won't miss my beautiful C++ diamond-shaped class graphs anymore.

  • int19h: Thanks for the feedback! I apologize for not clarifying, as that was exactly what I had in mind as well. In my opinion interfaces are perfect candidates for doing what you described, but just like you mentioned, it requires additional thought and the wrapping/delegating code. It works, but just not as elegant as multiple inheritance would've provided (comments I've received from other developers). My intention was to point people away from trying to reuse code just by inheritance, but look for alternatives in design patterns and other means (which more often than not will require the use of interfaces anyway). Point being - look beyond inheritance or trying to use interfaces to do what multiple inheritance does, if the purpose was to reuse code in a well-refactored manner; as sometimes the most effective path to an issue may not be a straight line.

  • Unfortunately one does not always have the option of properly refactoring code in order to avoid the need for multiple inheritance, as is the case when one is handed a boatload of crap to build an application upon and told "don't touch this". Your list of "reasons not to implement MI' reads like a list of excuses. MI would ruin backward compatibility and inter-operability with things that don't support it? Well I suppose that could be used as a reason for not moving forward in any field. This would have been a great excuse for not implementing .NET at all (think of all that great non-OO VB6 code it obsoleted). As for readability ... do you seriously think that a bunch of classes that need to inherit from both classes A and B are more readable because they were forced to inherit only from A and then each inheriting class had to duplicate all the code in B?

    The two great failures of .NET in the area of supporting the creation of maintainable code are 1) lack of typedefs and 2) lack of multiple inheritance. If someone doesn't understand why these two capabilities allow for creation of far more maintainable (and readable) code, then probably a blog is not the place to try to explain it. Rather, a few textbooks and some real-world experience might do the trick.

  • If you have control of the consumers and users of the code, or are writing code from scratch, it is even better to implement an interface that has just one readonly property, that being the class containing the concrete functionality you wish to share. E.g., the object you would have wrapped.

    One might call it something like an 'Interface Composite'.

    Assuming that this class is designed for the job (e.g., you don't wish to restrict access), then, the only disadvantage is the requirement for users to drill into this object with which the implementor is composed.

    Instead of:

    MyObject.SharedMethod

    you need:

    MyObject.SharedObject.SharedMethod

    As a tradeoff, you got rid of two huge problems with wrapping:

    *The need to either monkey code the wrappers/write a code generator/extend the language with syntactic sugar

    *The need to touch every object that implements your shared functionality if you add/change elements of the wrapper interface (e.g., exposing new methods of the wrapped object will require you to add the method to the interface, forcing you to visit each object that implements it).

Page 1 of 1 (6 items)
Leave a Comment
  • Please add 3 and 4 and type the answer here:
  • Post