General description

Basically, we will promote classes that are multiply-inherited into .NET interfaces. To give an example if in Eiffel you have:

class
	C
inherit
	A
	B
end

then in .NET you might have the following set of classes and interfaces:

class A implements IA
class B implements IB
class C implements IA and IB

interface IA
interface IB

This is one way of seeing it, but we could generate it in many different ways. It is important that:

Because of the last point, we decided that we would indeed generate the following set of classes and interfaces:

class A_IMP implements A
class B_IMP implements B
class C_IMP implements A and B

interface B
interface C

So if in Eiffel you have the following piece of code:

b: B
c: C

create c
b := c

It will translate in C# as:

B b;
C c;

/* Method 1 */
c = new C_IMP();
b = c;

/* Method 2 */
c = EiffelFactory.new_C();
b = c;

Note:
Creation is performed in two different ways. One through the use of a .NET constructor, and the second one through a factory call. Both will be implemented in case there is no way to use the .NET constructor mechanism, but we will try to stick as much as possible on the use of .NET constructor.

A real example

Let's suppose we have the following example. In which we do most of what can be seen in an Eiffel library that uses multiple inheritance.

class
	A
feature
	f is do ... end
	g (x: X) is do ... end
end
class B
inherit
	A
		rename f as h
		redefine g
		end
feature
	g (y: Y) is do ... end
end
class C
inherit
	A
end
class
	D
inherit
	B
		select
			g
		end
	C
		rename
			g as C_g
		select
			f
		end
end
deferred class F
feature
	f is do ... end
end
class E
inherit
	C
		undefine f end
	F
end
class X
end
class Y
inherit X
end

This is better described by the following BON diagram:

Let's have a look at what we should generate as an inheritance hierarchy:

The hierarchy is much simpler than the first method.

.interface A {
	f ();
	g (X x);
}

.class A_IMP: A {
	f () { call s_f (Current); };
	g (X x) { call s_g (Current, x); };

	static s_f (A a) { ... };
	static s_g (A a, X x) { ... };
}
.interface B: A {
	h ();
	g (Y y);
}

.class B_IMP: B {
	h {} {
		.override A::f
		call A_IMP::s_f (Current);
	};

	g (Y y) { call s_g (Current, y); };

	private __g (X x) {
		.override A::g
		call s_g ((Y) x))
	}

	static s_g (B b, Y y) { ... }
}
.interface C: A

.class C_IMP: C {
	f () { call A_IMP::s_f (Current); };
	g (X x) { call A_IMP::s_g (Current, x); };
}
.interface D: B, C {
	C_g (X x);
}

.class D_IMP: D {
	f() { call A_IMP::s_f (Current); };
	h () { call A_IMP::s_f (Current); };
	g (Y y) { call B_IMP::s_g (Current, y); };		
	C_g (X x) { call A_IMP::s_g (Current, x); };
}
.interface F {
	f();
}

.class F_IMP: F {
	f() { call s_f (Current); };

	static s_f (F f) { ... };
}
.interface E: C, F

.class E_IMP: E {
	f() {
		.override C::f
		call F_IMP::s_f (Current);
	};
	g(X x) { call A_IMP::s_g (Current, x); };
}

An explanation

Let's explain the previous example and let's determine what gets generated in the interfaces and what gets implemented in the implementation classes.

Interface

In the interface, will be generated the signature of:

Note:
We do not yet take into account local variables for inserting a feature in the interface.

Implementation

In the implementation class, we will generate all inherited features (defined in the parent interfaces) as stub to a call to their static definition. The static definition of a feature is generated in the implementation class corresponding to the origin class where the feature is actually written. This avoids to have too many static definitions everywhere that almost look a like.