Click here to Skip to main content
Licence CPOL
First Posted 27 Mar 2008
Views 59,867
Downloads 489
Bookmarked 51 times

Multiple Inheritance in C#

By | 27 Mar 2008 | Article
The attributes can be used to provide multiple inheritance functionality for C# classes

Introduction

This article shows a possible way to implement multiple inheritance (MI) within the C# code. It is absolutely explicit by design and it lacks some of the classic MI problems such as inheritance ordering.

Background

There are multiple ways to avoid or overcome the need for MI in C#. But sometimes, particularly for smaller projects (games, etc.), it would be more feasible to use its advantages. This was the case with my RL game. So I hope I found the way which should be reasonable and without being disturbed by the obstacles which native C# code puts in the way of multiple inheritance.

I don't consider multiple inheritance a good pattern of programming in general and it should be used only when the advantages of this pattern overweigh the disadvantages.

Class of Interest

First, we have to define our smart ancestor class which will handle all the functionality needed to implement MI. It is not much complicated. The missing inheritance will be compensated by the attribute MI.

using System;
using System.Collections.Generic;

namespace CSharpMultipleInheritance
{
    public class Ancestor : MarshalByRefObject
    {

When a class inherited from the Ancestor class is created, the set of its custom attributes instances is gathered and saved into a dictionary. This hash will be later used to access the inherited properties (methods, etc.)

private readonly Dictionary<Type, Object> attributes = null;

public Dictionary<Type, Object> Attributes
{
    get { return attributes; }
}

public Ancestor()
{
    attributes = GetAttributes(GetType());
}

private Dictionary<Type, Object> GetAttributes(Type sourceType)
{
    Object[] collection = sourceType.GetCustomAttributes(true);
    Dictionary<Type, Object> result = new Dictionary<Type, Object>();

    foreach (Object attribute in collection)
    {
        Type attributeType = attribute.GetType();

        if (result.ContainsKey(attributeType))
        {
            throw new Exception(string.Format(
            STR_DupliciteAttributeFound, attributeType.Name, sourceType.Name));
        }
        else
        {
            result.Add(attributeType, attribute);
        }
    }

    return result;
}

Them.. Methods

The Check method tests if a class is "inherited" from a specific attribute class. It's an equivalent of the native C# IS operator in our MI. As the code shows, it only checks whether the attribute type is present in the list of custom attributes.

public Boolean Check<TAttribute>()
{
return attributes.ContainsKey(typeof(TAttribute));
}

To determine whether an Ancestor inherited class contains all the attributes as the target type, that means yet another IS operator (this time a "real" one), the Is method is used. You need to supply a target Ancestor inherited class type to be checked against.

public Boolean Is<TAncestor>() where TAncestor : Ancestor
{
    Boolean result = true;
    Dictionary<Type, Object> sourceList = Attributes;
    Dictionary<Type, Object> destinationList = GetAttributes(typeof(TAncestor));

    foreach (KeyValuePair<Type, Object> destinationPair in destinationList)
    {
        result = result && sourceList.ContainsKey(destinationPair.Key);
    }

    return result;
}

The most important function which enables MI itself is the Use method. It retrieves the attribute class thus allowing to access its inner properties (methods, etc.).

public TAttribute Use<TAttribute>()
{
    if (Check<TAttribute>())
    {
        return (TAttribute)attributes[typeof(TAttribute)];
    }
    else
    {
        throw new ArgumentNullException(string.Format(
            STR_AttributeNotFound, typeof(TAttribute).Name, GetType().Name));
    }
}

Examples of Use

I have chosen the classic Wikipedia example of multiple inheritance. A different example can be seen in the zipped files attached.

The attribute classes (future inheritable candidates) are defined as standard attributes. Only public fields (instead of private fields with public properties defined) are used in this article to keep it obvious.

So let have the two classes; the Person class which indicates a person's name and age and the Worker class which defines a worker's salary.

public class Person : Attribute
{
    public String Name;
    public Int32 Age;
}

public class Worker : Attribute
{
    public Double Sallary;
}

Now the attributes are assigned to our MI class. This class has to be inherited from the Ancestor class (defined above) to use its MI potential.

[Person, Worker]
public class Musician : Ancestor
{
    public String Name; // this can be used as a musician stage name
}

The parent classes are then accessed via the Ancestor's methods (Check, Is and Use) as shown in the following example:

static void Main()
{
    Musician bono = new Musician();

    bono.Use<Worker>().Name = "Bono";
    bono.Use<Person>().Name = "Paul David Hewson";
    bono.Use<Person>().Age = 47;
        
    if (bono.Is<Musician>()) Console.WriteLine("{0} is musician.", 
    bono.Use<Worker>().Name);
         
    if (bono.Check<Person>()) Console.WriteLine("His age is {0}", 
    bono.Use<Person>().Age);
} 

Possible Enhancement

  1. All the attribute classes used can have a common interface or can be inherited from a common attribute class to separate them from other non-MI attributes.
  2. The list of FieldInfo classes (PropertyInfo, etc.) can be cached across all the regular fields and also attribute fields to create an even more real MI.

Limitations

  • The obvious limitation is that MI can't be used for the already inherited classes. I was trying to make the C# 3.0 extension methods which will be able to access any object. This proves to be impossible because there's no way of getting the custom attributes of a specific class instance from a general object.
  • Another limitation is that one "slot" of inheritance - the original C# one - is used. This should be compensated by MI itself.
  • An annoyance should be called the need to use method parenthesis for the MI methods. This is caused by the lack of the generic properties which are forbidden by C# design.
  • The compiler is unable to catch the inconsistencies in the Use<> class. This has to be handled by the Is<> checks or by the programmer himself/herself.

History

  • 2008-03-27: The generics were corrected in the article
  • 2008-03-26: The initial article was posted

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Smart K8

Software Developer
TelPro spol. s r.o.
Czech Republic Czech Republic

Member

I was born on August 29th of 1979 in the Czech Republic. I've been programming since I was 7 years old. These are the computer configurations I was using through the time:
 
Atari 800/XL - Atari Basic, Kyan Pascal, Assembler
Intel 80386/SX - Quick Basic, Assembler, various Borland Pascal products
Intel Celeron - Borland Delphi 5, Delphi 7, Free Pascal
AMD Athlon XP - Borland Delphi 8, Delphi 2005, JBuilder X, JBuilder 2005, Flash MX2004, MS VS2003
AMD Venice 64 - MS VS2005
AMD Athlon 64 X2 - MS VS2008, MS VS2010
 
I'm experienced in these fields of an interest:
 
Graphics
- 2D and 3D software emulation (algorithms)
- DirectX programming
- pixel & vertex shader programming
- color models
 
Mathematics
- huge number calculations
- scripting and compilers
 
Artificial intelligence (in-game)
- pathfinding
- genetic & neural algorithms
- NPC decision making
- virtual world simulations
 
Bussiness software
- databases MSSQL, Interbase, Firebird
- GUI design and icon creation
- cell phone & PDA applications
- webservices and 3-tier applications
- IrDA & Bluetooth communication
- expert in Windows API
- WiX installations
 
currently working at:
 
TelPro spol. s r.o. (Czech Republic)
 
contacts:
 
ICQ - 75861149
SKYPE - Smart_K8
EMAIL - smartk8@gmail.com

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
General3-tier applications in windows PinmemberMember 41411262:28 7 Oct '10  
QuestionMissing the point of MI? PinmemberDarchangel6:40 1 Apr '08  
AnswerRe: Missing the point of MI? PinmemberSmart K87:45 2 Apr '08  
AnswerRe: Missing the point of MI? Pinmemberpeterchen19:59 2 Apr '08  
GeneralRe: Missing the point of MI? [modified] PinmemberHRThomann20:39 2 Apr '08  
GeneralRe: Missing the point of MI? PinmemberPIEBALDconsult7:36 13 Dec '08  
GeneralRe: Missing the point of MI? PinmemberPIEBALDconsult7:22 13 Dec '08  
Indeed.
 
Interfaces are not a replacement for Multiple Inheritance.
And duck typing does not enforce the "like a duck" part.
 
Multiple Inheritance should be supported; if someone shoots his foot off, it's his foot, not mine.
 
Are we to cut down all the trees because some people climb them and fall out of them? I've never fallen out of any of the trees I've climbed.
GeneralImplantation - an alternative to Multiple Inheritance PinmemberHRThomann20:13 31 Mar '08  
GeneralRe: Implantation - an alternative to Multiple Inheritance PinmemberDarchangel6:24 1 Apr '08  
GeneralNifty PinmemberPIEBALDconsult5:25 27 Mar '08  
GeneralI like this PinmvpSacha Barber3:28 27 Mar '08  
GeneralToo clever PinmemberWilliam E. Kempf3:13 27 Mar '08  
GeneralRe: Too clever PinmemberSmart K83:43 27 Mar '08  
GeneralInteresting stuff! PinprotectorMarc Clifton2:48 27 Mar '08  
GeneralRe: Interesting stuff! PinmemberSmart K83:04 27 Mar '08  
GeneralRe: Interesting stuff! PinmemberMember 411671417:12 31 Mar '08  
Generalwith construction like this.. PinmemberSeishin#2:01 27 Mar '08  
AnswerRe: with construction like this.. [modified] PinmemberSmart K82:24 27 Mar '08  

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

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120529.1 | Last Updated 27 Mar 2008
Article Copyright 2008 by Smart K8
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid