4.7 KiB
2017-03-23 CLR Behavior for Default Interface Methods
Met today with
To discuss the CLR handling of default interface methods.
(1) Runtime handling of ambiguities
Question (1) is about what the CLR should do when the runtime does not find a most specific override for an interface method. For example, if this program is compiled (each interface being in a separate assembly)
public interface IA
{
void M();
}
public interface IB : IA
{
override void IA.M() { WriteLine("IB"); }
}
public interface IC : IA
{
}
class C : IB, IC
{
static void Main()
{
IA a = new C();
a.M();
}
}
and then subsequently IC
is changed
public interface IC : IA
{
override void IA.M() { WriteLine("IB"); }
}
This would cause an error at compile-time if class C
were recompiled (because there is no most specific override for IA.M
). However, what happens if this program is run without recompiling C
?
There are two most choices:
- The runtime selects, deterministically, between the implementations of
IA.M
inIB
andIC
; or - The runtime fails at some time before this invocation is run. a. At load time; or b. At the time of the invocation
We did not have consensus which approach is preferred; there are advantages and disadvantages to either. We will defer this issue until it can be discussed in a larger group.
Static methods
Static methods are already permitted in interfaces.
Protection levels
From the point of view of the CLR, once we start accepting a protection level other than private, we might as well accept all of them. The semantics are clear.
With the possible exception of protected
and its related protection levels. We would need to clearly define both the language and runtime constraints implied by this protection level. For example, is a protected
member in an interface accessible in a class that implements the interface? What would be the syntax in source (note that interface members are not inherited into classes that implement them)? What would be the verifiable form of the generated IL?
We tentatively agreed that the CLR will accept protection levels other than public
in interfaces, but open questions remain about protected
.
Sealed override
It is an open question whether a sealed override
should be permitted within an interface. Given the most specific override rule, such code could prevent unrelated sister interfaces from implementing the method.
If it were permitted, we need to check that there is a way to represent it in IL.
Non-virtual methods in interfaces
Non-virtual methods in interfaces are not a problem for the CLR, presuming we have a way to represent them. We need to check that existing interface methods have a newslot
bit set. We think so. If so, then we would simply omit that from non-virtual methods.
Implicit overrides in interfaces
We agree that the compiler-generated IL for an "implicit" override
public interface IA
{
void M();
}
public interface IB : IA
{
override void M() { WriteLine("IB"); }
}
should be roughly the same as for an "explicit" override
public interface IB : IA
{
override void IA.M() { WriteLine("IB"); }
}
In both cases the compiler produces a methodimpl
record for the overridden method(s). The CLR will not consider a method in an interface to be an implementation of another method without the methodimpl
bit. We should confirm that we are OK with the binary compatibility implications of this.
All override declarations should omit the newslot
bit to ensure no new vtable slot is allocated.
Open Question: Which interfaces are searched
There is an open question which interfaces are searched for implementations, and in which order. For example, if we have
interface IA { void M() {} }
interface IB: IA { override void M() {} }
class Base : IB { }
class Derived : Base, IA { }
Then is the override appearing in IB
the final override selected for class Derived
, even though IB
does not appear in the interface list for Derived
?
Open Question: Diamond Inheritance between Class and Interface
What should occur in cases involving diamond inheritance with classes and interfaces:
interface IA { void M() {} }
interface IB: IA { override void M() {} }
class Base : IA { void IA.M() { } }
class Derived : Base, IB { }
In this case neither Base
nor IB
is a subtype of the other. Is class Derived
legal? If so, what is its implementation of IA.M
?