|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Services
Chapters
Feature Zones
|
IntroductionThere are situations where multiple inheritance is the best (if not the only) choice when designing a certain model for our program or project. C# unfortunately does not support multiple inheritance and so we must look for ways to adapt our design to make possible its implementation. But C# does offer resources and tricks that can be used to simulate multiple inheritance without radically redesigning our class model, with only adding several auxiliary methods and classes and taking one or two precautions while coding them. I propose a design pattern that lets us simulate multiple inheritance in a C# program in a way that produces classes that behave almost like they were really extended from two or more parent classes. We will face classical multiple inheritance problems too and will see how to address them. BackgroundMost recent languages like C#, Java, Delphi, have simple inheritance. They don't have multiple inheritance because their designers had to choose between have it in and have all the problems it comes with, or get it out of the language putting away all those problems, and introduce a versatile and less problematic substitute like interfaces and interface inheritance. Multiple inheritance has multiple problems that we will discuss later. But those problems are quite pathological and do not arise in any program in which multiple inheritance could be useful or could be the most appropriate design. There are traditional ways to emulate multiple inheritance with interface inheritance with more or less success. Let's suppose we have two classes,
class A
{
m1();
}
class B
{
m2();
}
class C : A, B
{
m1();
m2();
}
That code is impossible in C#. The following is the classical workaround to deal with this:
class A
{
m1();
}
interface IB
{
m2();
}
class B : IB
{
m2();
}
class C : A, IB
{
B BObject;
m1();
m2() { BObject.m2(); }
}
In this code, we make class But this solution has several problems. One of them is the fact that we can't use The simulated multiple inheritance pattern for C#We have seen that we cannot simulate multiple inheritance completely using only interfaces and simple inheritance. We need something more, and C# happens to have that. Let's see. We have these objectives:
Here is the basic idea: We will create two auxiliary classes, class A
{
m1();
}
class B
{
m2();
}
class Aaux : A
{
m1();
}
class Baux : B
{
m2();
}
Our new class class C
{
Aaux APart;
Baux BPart;
m1()
{
APart.m1();
}
m2()
{
BPart.m2();
}
}
So every class Aaux : A
{
C CPart;
m1();
}
class Baux : B
{
C CPart;
m2();
}
And finally we arrive at the final trick. We will redefine the implicit casting operator for class
Again, we will redefine the implicit casting operator for class This is the final look:
class Aaux : A
{
C CPart;
m1();
static implicit operator C(Aaux a)
{
return a.CPart;
}
}
class Baux : B
{
C CPart;
m2();
static implicit operator C(Baux b)
{
return b.CPart;
}
}
class C
{
Aaux APart;
Baux BPart;
m1()
{
APart.m1();
}
m2()
{
BPart.m2();
}
static implicit operator A(C c)
{
return c.APart;
}
static implicit operator B(C c)
{
return c.BPart;
}
}
Now given that code, we can use However, there is another step we can take that will allow us to reduce in on the number of extra classes required and will let one of the parent classes have
We only have to make Of course, properties are fully compatible with this pattern, since they behave like methods. So parent classes may have as many Using the codeLet's see an example. We are a computer dealer that buys computer hardware from major vendors and sells it to end users. However, we often get out of stock and we avoid losing customers by buying from rival computer shops and re-selling to our customers. We are not the only ones with that policy, so other shops often purchase our goods for selling them later. Our program has two arrays - in one of them we keep all our vendors, and in the other we keep all our customers. Rival shops are both vendors and customers. This would be the idea: class Vendor
{...}
class Customer
{...}
class Shop : Vendor, Customer
{...}
And this could be the final code (it's available in the download): /// <summary>
/// A computer manufacturer. They resupply us.
/// </summary>
public class Vendor
{
string id;
public string VendorId
{
get
{
return id;
}
set
{
id = value;
}
}
public Vendor(string vendorId)
{
id = vendorId;
}
public virtual void AskForRessuply()
{
Console.WriteLine("Please ressuply me, vendor "+id+".");
}
}
/// <summary>
/// A customer. We send them their purchased goods.
/// </summary>
public class Customer
{
string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
}
}
public Customer(string customerName)
{
name = customerName;
}
public virtual void SendOrder()
{
Console.WriteLine("Dear "+name+": We are sending your goods.");
}
}
/// <summary>
/// The auxiliary class that redefines Customer.
/// </summary>
internal class CustomerAux : Customer
{
// <--- It has a link to the Shop object that contains it.
internal Shop shopPart;
internal CustomerAux(string customerName) : base (customerName)
{
}
// We declare the implicit casting operator for returning
// shopPart when a Shop object is expected.
static public implicit operator Shop(CustomerAux c)
{
return c.shopPart;
}
}
/// <summary>
/// We consider a shop like a Vendor and a Customer,
/// for we both ask them to resupply us,
/// or they purchase our goods.
/// </summary>
public class Shop : Vendor // <-- It inheirs only from Vendor...
{
CustomerAux customerPart; // ...but has a CustomerAux object inside.
// Shops have an address in addition to the vendor id
// and the customer name inherited from Vendor and Customer.
string address;
public string Address
{
get
{
return address;
}
set
{
address = value;
}
}
// Here we are 'redirecting' property Name to the customerPart object.
public string Name
{
get
{
return customerPart.Name;
}
set
{
customerPart.Name = value;
}
}
// The Shop constructor.
public Shop(string vendorId, string customerName, string shopAddress) :
base (vendorId)
{
// We create and bind the CustomerAux object to this one.
customerPart = new CustomerAux(customerName);
customerPart.shopPart = this;
address = shopAddress;
}
// Here we are redirecting Customer.SendOrder to the customerPart object.
public virtual void SendOrder()
{
customerPart.SendOrder();
}
// We redefine the implicit casting operator for returning
// customerPart when a Customer object is expected.
static public implicit operator Customer(Shop s)
{
return s.customerPart;
}
}
// An example of use of the Vendor, Customer and Shop classes.
class EntryPoint
{
static void Main(string[] args)
{
Vendor ibm = new Vendor("32FK-IBM");
Vendor hp = new Vendor("1138-HP");
Customer mrSimpson = new Customer("Mr. Simpson");
Customer mrGates = new Customer("Mr. Gates");
Shop joys =
new Shop("1979-JCS", "Joy's Computer Shop", "123, Fake St.");
Vendor[] vendors = {ibm, hp, joys};
foreach(Vendor ven in vendors)
ven.AskForRessuply();
Customer[] customers = {mrSimpson, mrGates, joys};
foreach(Customer cus in customers)
cus.SendOrder();
Console.ReadLine();
}
}
Points of interest - classical problemsOne of the most important problems of multiple inheritance is caused by this situation:
Class Now class D d = new D();
d.m1();
...this code? B d = new D();
d.m1();
...and this code? C d = new D();
d.m1();
Languages and compilers supporting multiple inheritance solve this in one way or the other. But this makes compiling, debugging and understanding the code more difficult. Often the version of the implementation of However, this pattern solves to certain degree that problem, because we can choose what implementation will be run. Assuming our
Last commentMultiple inheritance was not included in C# for important reasons. However, I sometimes miss it and that is the reason why I proposed this pattern. I think it may be useful for those who find more advantages in its application than disadvantages. I'd like to see your comments on it, telling problems I didn't see and advices for enhancement. History
| ||||||||||||||||||||