dotnet / dotnet/csharpstandard

Should the spec explain interface mapping for variant interfaces?

Open
#297 1 comment 4 reactions 0 assignees View on GitHub
Dominant language
C#
Stars
815
Forks
99
Avg merge
1d 14h
Merged PRs (30d)
16

Description

Consider the following code (especially notice that the interface is covariant):

```c#
interface I { void M(); }

class A1 {}
class A2 : A1 {}

class B1 : I
{
void I.M() => Console.WriteLine("B1.I.M");
}

class B2 : B1, I
{
void I.M() => Console.WriteLine("B2.I.M");
}

I x = new B2();
x.M();
```

This code prints `B2.I.M`, but I could not find justification for that behavior in the C# spec.

When the above code is compiled, [it produces the expected IL](https://sharplab.io/#v2:C4LglgNgPgAgDAAhgRgNwFgBQWwDtgCmATgGYCGAxgQgJIA8A9gK7AIAqAfAgN5IAsCALIAKAJSoEAXyxYYAJgQBBZD2mZ5ShSCUrua2QoBCK7fWUcs3LAhv9adcwDoRohAF4uKAJzCARMcczZA5nX3EsfXUjLQRjABp7RTkLTCtMWzszZOcxd09kH385QIdswTCMTEiUADYkBRQAdktrW1q7F1abNIyMoK4AD3cEXAIAd1i5MUqMgEgBnPD02zVJIA=), where `I.M` contains an `.override` for `I`.

When executed on the CLR, it behaves according to [ECMA-335 §II.12.2 Implementing virtual methods on interfaces](https://www.ecma-international.org/publications/files/ECMA-ST/ECMA-335.pdf#%5B%7B%22num%22%3A1997%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C87%2C257%2C0%5D), which says (emphasis mine):

> When an interface method is invoked, the VES shall use the following algorithm to determine the
appropriate method to call:
> * Beginning with the runtime class of the instance through which the interface method
is invoked, using its interface table as constructed above […]:
> 1. For each method in the list associated with the interface method, if there exists a
method whose generic type arguments match exactly for this instantiation (or
there are no generic type parameters), then call the first method. […]
> 2. **Otherwise, if there exists a method in the list whose generic type parameters
have the correct variance relationship, then call the first such method in the list.**
> 3. If no method is found in this class, return to step 1 with the next class in the
inheritance chain (i.e. the Extends field of the current class)
> 4. If no method is found, then raise System.InvalidCastException

But [a similar section of ECMA-334, §18.6.5 Interface mapping](https://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf#%5B%7B%22num%22%3A2942%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C55%2C737%2C0%5D) says:

> The implementation of a particular interface member `I.M`, where `I` is the interface in which the member `M` is declared, is determined by examining each class or struct `S`, starting with `C` and repeating for each successive base class of `C`, until a match is located:
> * If `S` contains a declaration of an explicit interface member implementation that matches `I` and `M`, then this member is the implementation of `I.M`.
> * Otherwise, if `S` contains a declaration of a non-static public member that matches `M`, then this member is the implementation of `I.M`. If more than one member matches, it is unspecified which member is the implementation of `I.M`. This situation can only occur if `S` is a constructed type where the two members as declared in the generic type have different signatures, but the type arguments make their signatures identical.
>
> […]
>
> For purposes of interface mapping, a class or struct member `A` matches an interface member `B` when:
> * `A` and `B` are methods, and the name, type, and formal parameter lists of `A` and `B` are identical.
> * […]

It's not quite clear to me what the above part of the spec says about the code in the example, because I think it doesn't define what matching for explicit interface members means.

I think the spec should be clarified, so that it properly explains how the above code should behave.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.