.NET: interfaces that inherit from multiple base interfaces
For my link archive:
- [WayBack] .net – interface inheriting multiple interfaces: how is this handled by a C# compiler? – Stack Overflow.
- [WayBack] New Delphi language feature: Multiple inheritance for interfaces in Delphi for .NET.
Q
Recently I found out that C# allows for
For instance, the
IScreen
in Caliburn.Micro does this in http://caliburnmicro.codeplex.com/SourceControl/latest#src/Caliburn.Micro/IScreen.csnamespace Caliburn.Micro { public interface IScreen : IHaveDisplayName, IActivate, IDeactivate, IGuardClose, INotifyPropertyChangedEx { } }
I get why this is useful, as it implies that a class implementing
IScreen
also needs to implement the other interfaces.But I wonder how the C# handles that compiler and run-time wise.
A little background/context of this question:
I come from a background where interfaces define a method-table, and that classes implementing interfaces have both their own method table, and pointers to the method tables of the interfaces they implement.
Sub questions swirling my mind stem from various multiple class inheritance discussions I had with people in the past, of which I think they apply to this case as well:
- Having an interface be able to inherit from multiple base interfaces, how would the order of methods in that table be?
- What if those interfaces have common ancestors: would those methods appear multiple times in the table?
- What if those interfaces have different ancestors, but similar method names?
(I’m using the word methods here, implying that a property defined in an interface will have a get_ or set_ method).
Any insight into this is much appreciated, as well as tips on how to phrase this question better.
A1
First of all, let’s be explicit in saying that “interface inheritance” is not quite the same thing as class-based inheritance (and using the word “inheritance” for both is perhaps misleading).
That’s because interfaces cannot be instantiated on their own, so the compiler/runtime pair does not have to keep track of how to make a virtual call for standalone interface types (e.g. you don’t need to know how to call
IEnumerable.GetEnumerator
— you just need to know how to call it on a specific type of object). That allows for handling things differently at compile time.Now I don’t actually know how the compiler implements “interface inheritance”, but here’s how it could be doing it:
Having an interface be able to inherit from multiple base interfaces, how would the order of methods in that table be?
It’s not necessary for the “derived” interface to have a method table that includes the methods from all of its ancestor interfaces because it does not actually implement any of them. It’s enough for each interface type to only have a table of methods it defines itself.
What if those interfaces have common ancestors: would those methods appear multiple times in the table?
Given the answer to the previous question, no. In the end a concrete type will only implement
IFoo
just once, regardless of how many timesIFoo
appears in the “hierarchy” of implemented interfaces. Methods defined inIFoo
will only appear inIFoo
‘s bookkeeping tables.What if those interfaces have different ancestors, but similar method names?
Again, no problem. You need appropriate syntax to tell the compiler “here’s how to implement
IFoo.Frob
and here’sIBar.Frob
“, but since methods ofIFoo
andIBar
will be mapped in separate tables there are no technical issues.Of course this leaves the question “how are methods dispatched at runtime?” unanswered. But it’s not that difficult to imagine a possible solution: each concrete type
C
has pointers to one method table per interface it implements. When it’s time to make a virtual method call the runtime looks at the concrete type, finds the table for the interface whose method is going to be called (the type of the interface is known statically) and makes the call.
A2
I am unable to speak to how the official CLR does it.. but the Rotor distribution aggresively overlays common interface ancestors on top of one another in an objects vtable. It also allocates extra SLOTs into the concrete objects vtable where appropriate thereby reducing the need to jump from concrete type to interface vtable and then to the implementation.. the method offset is calculated at JIT time. If this optimization cannot be performed, then a single method can occupy the vtable more than once.
So the answer is (in regards to Rotor anyway), that it really is an implementation detail and any overlaying/optimizations etc are left completely up to what the compiler decides is best at the time it compiles the type.
–jeroen
Leave Your Comment