Click here to Skip to main content
6,822,613 members and growing! (20,374 online)
Email Password   helpLost your password?
Languages » C# » Attributes     Intermediate License: The Code Project Open License (CPOL)

Multiple Inheritance in C#

By Smart K8

The attributes can be used to provide multiple inheritance functionality for C# classes
C# (C#2.0, C#3.0), Windows, .NET (.NET2.0, .NET3.0, .NET3.5), Visual-Studio (VS2005, VS2008)
Posted:27 Mar 2008
Views:27,579
Bookmarked:42 times
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
22 votes for this article.
Popularity: 5.71 Rating: 4.26 out of 5
1 vote, 4.5%
1
1 vote, 4.5%
2
4 votes, 18.2%
3
5 votes, 22.7%
4
11 votes, 50.0%
5

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


Member
I was borned on August 29th of 1979 and I'm from the Czech Republic. I'm programming since I was 7 years old. These are the computer configurations I was using thru 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 (especially) 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
- 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 - drahokoupilova@telpro.cz (work)
Occupation: Software Developer (Senior)
Company: TelPro spol. s r.o.
Location: Czech Republic Czech Republic

Other popular C# articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 17 of 17 (Total in Forum: 17) (Refresh)FirstPrevNext
GeneralMissing the point of MI? PinmemberDarchangel7:40 1 Apr '08  
GeneralRe: Missing the point of MI? PinmemberSmart K88:45 2 Apr '08  
GeneralRe: Missing the point of MI? Pinsupporterpeterchen20:59 2 Apr '08  
GeneralRe: Missing the point of MI? [modified] PinmemberHRThomann21:39 2 Apr '08  
GeneralRe: Missing the point of MI? PinmemberPIEBALDconsult8:36 13 Dec '08  
GeneralRe: Missing the point of MI? PinmemberPIEBALDconsult8:22 13 Dec '08  
GeneralImplantation - an alternative to Multiple Inheritance PinmemberHRThomann21:13 31 Mar '08  
GeneralRe: Implantation - an alternative to Multiple Inheritance PinmemberDarchangel7:24 1 Apr '08  
GeneralNifty PinmemberPIEBALDconsult6:25 27 Mar '08  
GeneralI like this PinmvpSacha Barber4:28 27 Mar '08  
GeneralToo clever PinmemberWilliam E. Kempf4:13 27 Mar '08  
GeneralRe: Too clever PinmemberSmart K84:43 27 Mar '08  
GeneralInteresting stuff! PinsupporterMarc Clifton3:48 27 Mar '08  
GeneralRe: Interesting stuff! PinmemberSmart K84:04 27 Mar '08  
GeneralRe: Interesting stuff! PinmemberMember 411671418:12 31 Mar '08  
Generalwith construction like this.. PinmemberSeishin#3:01 27 Mar '08  
AnswerRe: with construction like this.. [modified] PinmemberSmart K83:24 27 Mar '08  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

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

PermaLink | Privacy | Terms of Use
Last Updated: 27 Mar 2008
Editor: Deeksha Shenoy
Copyright 2008 by Smart K8
Everything else Copyright © CodeProject, 1999-2010
Web22 | Advertise on the Code Project