AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=1452&pId=-1
Simulating Multiple Inheritance by Delegation
page
by Joseph Chahine
Feedback
Average Rating: 
Views (Total / Last 10 Days): 26336/ 44

Introduction

Multiple Inheritance is still not considered to be a best practice in most cases. That is why it is not supported in both .NET and Java programming languages. But sometimes, it can be helpful. In this article, I will explain a way to simulate Multiple Inheritance in .NET by using the Delegation technique.

What is delegation?

As defined in the famous book "Design Patterns - Elements of Reusable Object-Oriented Software," Delegation is "a way of making composition as powerful for reuse as inheritance. In delegation, two objects are involved in handling a request: a receiving object delegates operations to its delegate. This is analogous to subclasses deferring requests to parent classes. But with inheritance, an inherited operation can always refer to the receiving object through the member variable in C++ and self in Smalltalk. To achieve the same effect with delegation, the receiver passes itself to the delegate to let the delegated operation refer to the receiver."

In short, delegation can be viewed as a relationship between objects where one object forwards (or delegate) certain method calls to another object.

When would you need it?

A simple example would be to consider that we are building a distributed application and we have a class called MyCustomCollection that inherits from System.Collections.ArrayList. We want to have instances of the MyCustomCollection class marshalled from a server to another across a firewall. In .NET, we have to derive MyCustomCollection from the MarshalByRefObject class, but is not MyCustomCollection a subclass of System.Collections.ArrayList already?! Unfortunately, we will not be able to derive MyCustomCollection from MarshalByRefObject because it can only inherit from one class, which is System.Collections.ArrayList.

In such cases, Multiple Inheritance could be helpful. Please note that there is no situation where multiple inheritance is necessary. Everything done with C++ can still be done with VB.NET or C#.NET. In this article I am just offering a workaround.

How to use it?

As per the definition above, the receiving object in our example is an instance of the MyCustomCollection class, so we should derive it from MarshalByRefObject. To make it act as it is also inheriting from System.Collections.ArrayList, we will create a class called MyCustomCollectionSlave that inherits from System.Collections.ArrayList.

MyCustomCollectionSlave will act as an object delegate to MyCustomCollection (MyCustomCollectionSlave was not called MyCustomCollectionDelegate to avoid confusion with the .NET System.Delegate type).

Now we need to inject (or "inherit") the behaviors of MyCustomCollectionSlave into MyCustomCollection. This why we define a field within MyCustomCollection called InnerList of type MyCustomCollectionSlave and we write dumb entries for all methods of MyCustomCollectionSlave in MyCustomCollection. These methods will only be responsible for forwarding calls to the MyCustomCollectionSlave corresponding methods. The following listings make things easier to understand.

Listing 1

[C#]

//This class plays the role of a delegate
internal class MyCustomCollectionSlave : System.Collections.ArrayList
{
    #region "Methods"
 
        public object GetSomething() {
            //Write some code here
        }
 
    #endregion
}
public class MyCustomCollection : System.MarshalByRefObject
{
    
#region "Fields"
 
    private MyCustomCollectionSlave _InnerList;
 
#endregion
 
#region "Properties"
 
    //Through this property, all delegation is done.
    private MyCustomCollectionSlave InnerList {
        get {
                return this._InnerList;
            }
    }
 
    //The properties below help delegating ArrayList's built-in properties.

     public int Count {
        get {
            return this.InnerList.Count;
        }
    }
 
    public bool IsSynchronized {
        get {
            return this.InnerList.IsSynchronized;
        }
    }
 
    public object SyncRoot {
        get {
            return this.InnerList.SyncRoot;
        }
    }
 
    public bool IsFixedSize {
        get {
            return this.InnerList.IsFixedSize;
        }
    }
     
    public bool IsReadOnly {
        get {
            return this.InnerList.IsReadOnly;
        }
    }
 
    public object this[int index] {
        get {
            return this.InnerList[index];
        }
        set {
             this.InnerList[index] = value;
        }
    }
 
#endregion
 
 
#region "Constructors"
 
    public MyCustomCollection() {
        this._InnerList = new MyCustomCollectionSlave();
    }
 
#endregion
 
#region "Methods"
 
    //This method simply forwards calls to the GetSomething() method in InnerList
    public object GetSomething() {
       return this.InnerList.GetSomething();
    }
 
    public void CopyTo(System.Array array, int index)  {
        this.InnerList.CopyTo(array, index);
    }
 
    public System.Collections.IEnumerator GetEnumerator() {  
        return this.InnerList.GetEnumerator();
    }
 
    public int Add(object value) {
        return this.InnerList.Add(value);
    }
 
    public void Clear() {
        this.InnerList.Clear();
    }
 
    public bool Contains(object value) {
        return this.InnerList.Contains(value);
    }
 
    public int IndexOf(object value) { 
        return this.InnerList.IndexOf(value);
    }
 
    public void Insert(int index, object value) {
        this.InnerList.Insert(index, value);
    }
 
    public void Remove(object value)  {
        this.InnerList.Remove(value);
    }
 
    public void RemoveAt(int index) {
        this.InnerList.RemoveAt(index);
    }
 
    public object[] ToArray() {
        return ((System.Collections.ArrayList)InnerList).ToArray();
    }
 
    public object[] ToArray(System.Type type) {
        return (object[])(((System.Collections.ArrayList)InnerList).ToArray(type));
    }
 
#endregion
 
}

[VB]

'This class plays the role of a delegate
Friend Class MyCustomCollectionSlave
    Inherits System.Collections.ArrayList

 #Region "Methods"
 
    Public Function GetSomething() As Object 
        'Write some code here
    End Function

 #End Region

 End Class
 
Public Class MyCustomCollection
    Inherits MarshalByRefObject
    
#Region "Fields"
 
    Private _InnerList As MyCustomCollectionSlave
 
#End Region
 
#Region "Properties"
 
    'Through this property, all delegation is done.
    Private ReadOnly Property InnerList() As MyCustomCollectionSlave
        Get
            Return Me._InnerList
        End Get
    End Property
 
    'The properties below help delegating ArrayList's built-in properties.
 
    Public ReadOnly Property Count() As Integer
        Get
            Return Me.InnerList.Count
        End Get
    End Property
 
    Public ReadOnly Property IsSynchronized() As Boolean
        Get
            Return Me.InnerList.IsSynchronized
        End Get
    End Property
 
    Public ReadOnly Property SyncRoot() As Object 
        Get
            Return Me.InnerList.SyncRoot
        End Get
    End Property
 
    Public ReadOnly Property IsFixedSize() As Boolean 
        Get
            Return Me.InnerList.IsFixedSize
        End Get
    End Property
 
    Public ReadOnly Property IsReadOnly() As Boolean 
        Get
            Return Me.InnerList.IsReadOnly
        End Get
    End Property
 
    Default Public Property Item(ByVal index As IntegerAs Object 
        Get
            Return Me.InnerList.Item(index)
        End Get
        Set(ByVal value As Object)
            Me.InnerList.Item(index) = value
        End Set
    End Property
 
#End Region
 
 
#Region "Constructors"
 
    Public Sub New()
        Me._InnerList = New MyCustomCollectionBase
    End Sub
 
#End Region
 
#Region "Methods"
 
 
    //This method simply forwards calls to the GetSomething() method in InnerList
    Public Function GetSomething() As Object
        Return Me.InnerList.GetSomething()
    End Function
 
    'The methods below help delegating ArrayList's built-in methods.

     Public Sub CopyTo(ByVal array As System.Array, ByVal index As Integer) 
        Me.InnerList.CopyTo(array, index)
    End Sub
 
    Public Function GetEnumerator() As System.Collections.IEnumerator 
        Return Me.InnerList.GetEnumerator
    End Function
 
    Public Function Add(ByVal value As ObjectAs Integer 
        Return Me.InnerList.Add(value)
    End Function
 
    Public Sub Clear()
        Me.InnerList.Clear()
    End Sub
 
    Public Function Contains(ByVal value As Object) As Boolean 
        Return Me.InnerList.Contains(value)
    End Function
 
    Public Function IndexOf(ByVal value As Object) As Integer 
        Return Me.InnerList.IndexOf(value)
    End Function
 
    Public Sub Insert(ByVal index As IntegerByVal value As Object) 
        Me.InnerList.Insert(index, value)
    End Sub
 
    Public Sub Remove(ByVal value As Object) 
        Me.InnerList.Remove(value)
    End Sub
 
    Public Sub RemoveAt(ByVal index As Integer)
        Me.InnerList.RemoveAt(index)
    End Sub
 
    Public Function ToArray() As Object()
        Return CType(Me.InnerList, ArrayList).ToArray()
    End Function
 
    Public Function ToArray(ByVal type As System.Type) As Object() 
        Return CType(Me.InnerList, ArrayList).ToArray(type)
    End Function
 
#End Region
 
End Class

The example above is a simple one. A good practice would be to create a generic base class the same way we created MyCustomCollection, call it for example MyCustomCollectionBase(Of T). You can use delegation to simulate multiple inheritance within this class.

Note that we can delegate the behaviors of more than one class; this allows simulating inheritance not only from a couple of classes as in our example above, but also from any number of classes.

Then we can derive new classes from MyCustomCollectionBase(Of T). This has the same effect as deriving them from the classes inherited and delegated by MyCustomCollectionBase. For example, we can create a class called MyOrderCollection that is derived from MyCustomCollectionBase (Of OrderInfo).

Conclusion

Although Multiple Inheritance presents a complicated problem when two or more parent classes have overlapping behaviors or attributes, I still find it useful especially when we think about combining multiple classes. I hope that the next versions of Visual Studio will have an option to enable multiple inheritance support. Starting with an experimental version seems to be a nice idea.



©Copyright 1998-2024 ASPAlliance.com  |  Page Processed at 2024-04-19 11:47:52 AM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search