Introduction
Many people out there may not be impressed or happy about this, but yes, there is a way to simulate multiple inheritance in C# 3.0. And more importantly, it is incredibly simple to implement.
Using the Code
All you require is an interface
to use as a marker and some extension methods.
Here, we have the simplest of scenarios, a Logger
“class” that any class can “inherit” from.
public interface Logger
{
}
public static class LoggerImpl
{
public static void Log(this Logger instance, string msg)
{
Debug.WriteLine(msg);
}
public static void LogError(this Logger instance, string msg)
{
Debug.WriteLine(string.Format("ERROR: {0}", msg));
}
}
Figure 1. Implementation using the Interface and Extension methods.
public partial class Form1 : Form, Logger
{
private void button1_Click(object sender, EventArgs e)
{
this.Log("Button1 Clicked");
}
private void button2_Click(object sender, EventArgs e)
{
this.LogError("Button2 Clicked");
}
}
Figure 2. WinForm usage.
public class Car: Vehicle, Logger
{
private override void Drive()
{
this.Log("I’m driving now!");
}
}
Figure 3. Business Object usage.
I don't believe it can get any easier than this. Obviously, you can get much more complex and sophisticated as the solution necessitates. The key is to understand the new feature of C# 3.0, extension methods. I won't go into the specific of this C# feature as it is well documented in many places.
There is one caveat to the implementation which is that you must use the 'this
' keyword to access the extension methods from within the class itself. Not a hefty price to pay for this feature.
Points of Interest
As a further example, I've used this approach in ASP.NET for a custom Session handler class (SessionSupport
) which leverages generics, constraints and lambdas to manage a specific type of business object derivative. This particular implementation is also aware of the fact that the SessionSupport
class is always applied to ASP.NET Page
classes and therefore can safely cast the ‘this
’ instance parameter to a Page
type and access all of the Page
specific members. I opted to document this feature using a basic Logger
sample to keep things as focused on the issue at hand as possible.
Another approach would be to include a few methods on the interface
forcing the consumer base class to implement these functions providing some access to pertinent fields, properties, or methods of the base class and avoiding the blind cast.
Follow Up
I suppose the simplicity of the example has worked against me. :)
In this situation, assuming you wanted every class to expose the Logging
methods, as Patrik_J points out, which isn't unreasonable, you could simply add the extension methods to the 'object
' class. But that misses the point of the demonstration - my bad.
The ASP.NET Session code that I've applied this technique to is far too complex (and proprietary) for this forum, but I can try to give another example of the usage.
Let’s say, for example, that you want to have an automated security feature that could be applied to a subset of selected Windows controls in a WinForms application.
For example:
public class MyForm : Form, SecurityMgr
{
...
}
The problem is as such; you want a method exposed from certain custom controls called ‘ApplySecurity
’ which takes a security token and dynamically enables/disables child controls contained on it. I won't go into the details of how the tokens and controls are related. I'll leave that for another day. You have classes derived from System.Windows.Forms.Form
and from System.Windows.Forms.ToolStripContainer
and other classes that are derivatives of System.Windows.Forms.Control
which contains the Controls
property. You obviously don't want to derive a class from Form
and from ToolStripContainer
and from those others, simply to embed the new method. And then derive your custom Controls
from it. Nor would you want to implement ‘ApplySecurity
’ as an extension method on all System.Windows.Forms.Control
as other classes have no context for this usage. So, at this point, you can apply this pattern.
I hope this adds some clarity to the benefits of this technique.
Polymorphism Addressed - Kinda Short
public interface Logger2 : Logger
{
}
public static class Logger2Impl
{
public static void LogError(this Logger2 logger, string msg)
{
System.Diagnostics.Debug.WriteLine(string.Format("NEW ERROR: {0}", msg));
}
}
I've made an attempt to address the polymorphism issue that was mentioned by Nick Darnell.
This solution still needs some thought - but it immediately interested me as it allows for reuse of the 'overridden' method. The code is again fairly straight forward and I'm using the simplest of scenarios to illustrate the point. By simply inheriting a new interface
from the existing interface
and 'overriding' the method of interest, you can mimic the desired behavior. Now all that is left is to switch your consumer class to derive from Logger2
rather than Logger
and you can leverage this custom implementation in other classes (the additional benefit of interest). The actual code in the consumer
class or the consumer of the consumer
class invokes the LogError
and Log
methods in the appropriate base/derived classes (LogError
in Logger2Impl
and Log
in LoggerImpl
) without knowing the difference.
History
- 13th May, 2008: Initial post
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.