Click here to Skip to main content
15,891,253 members
Articles / Programming Languages / C#
Article

Getting Closer to Multiple Inheritance in C# 3.0

Rate me:
Please Sign up or sign in to vote.
3.81/5 (13 votes)
13 May 2008Public Domain4 min read 29.6K   15   16
How to implement multiple inheritance in C# 3.0

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.

C#
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.

C#
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.

C#
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:

C#
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

C#
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

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
Architect
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 3 Pin
Nick Alexeev19-Sep-14 13:46
professionalNick Alexeev19-Sep-14 13:46 
GeneralNot impressed (feedback) Pin
Oleg Shilo26-May-08 14:30
Oleg Shilo26-May-08 14:30 
GeneralAwfully clever. Pin
Ashaman15-May-08 10:08
Ashaman15-May-08 10:08 
GeneralHere's a bone...=) Pin
Member 345750613-May-08 10:40
Member 345750613-May-08 10:40 
GeneralRe: Here's a bone...=) Pin
Audaxis13-May-08 12:02
Audaxis13-May-08 12:02 
QuestionWhat do you win? Pin
Patric_J13-May-08 9:06
Patric_J13-May-08 9:06 
AnswerRe: What do you win? Pin
Audaxis13-May-08 10:28
Audaxis13-May-08 10:28 
GeneralNot impressed Pin
U-P-G-R-A-Y-E-D-D13-May-08 7:33
U-P-G-R-A-Y-E-D-D13-May-08 7:33 
GeneralRe: Not impressed Pin
Audaxis13-May-08 8:18
Audaxis13-May-08 8:18 
GeneralRe: Not impressed Pin
Audaxis13-May-08 11:48
Audaxis13-May-08 11:48 
GeneralRe: Not impressed Pin
U-P-G-R-A-Y-E-D-D13-May-08 12:41
U-P-G-R-A-Y-E-D-D13-May-08 12:41 
GeneralRe: Not impressed Pin
Audaxis13-May-08 13:56
Audaxis13-May-08 13:56 
GeneralRe: Not impressed Pin
U-P-G-R-A-Y-E-D-D13-May-08 15:19
U-P-G-R-A-Y-E-D-D13-May-08 15:19 
GeneralRe: Not impressed Pin
Audaxis13-May-08 16:11
Audaxis13-May-08 16:11 
GeneralRe: Not impressed Pin
S. Senthil Kumar13-May-08 21:28
S. Senthil Kumar13-May-08 21:28 
Shane Battrick wrote:
And as for vTables - that is an underlying runtime implementation detail outside of the scope of this. You can have polymorphism without vTables - not sure why you injected that into a discussion of this nature.


Yes, but there must be some form of "runtime" binding, and I don't see how you can do that with extension methods, which are simply static methods in a static class.

Regards
Senthil [MVP - Visual C#]
_____________________________
My Blog | My Articles | My Flickr | WinMacro

GeneralRe: Not impressed Pin
ttuBrant18-May-09 19:44
ttuBrant18-May-09 19:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.