csharplang/meetings/2017/LDM-2017-05-17.md
2017-05-30 15:56:12 -07:00

3.5 KiB

C# Language Design Notes for May 17, 2017

Raw notes, yet to be cleaned up - read at your own peril

Conflicting override

If at runtimes you have ambiguous overrides

interface I1 { void M1(); }

interface I2 : I1 { override void M1() { ... }} 

interface I3 : I1 { override void M1() { ... } } 

class C : I2, I3 {} // what happens when you load?

C x;
x.M1(); // what happens when you call?

This can happen through certain orders of compilation.

Options:

  1. throw on load
  2. throw on call (like Java)
  3. pick arbitrarily (but consistently)
  4. bake in what you meant at compile time
    1. always use the baked in, error if it's gone
    2. pick the unique one, fallback to baked in if unsuccessful

Baking in causes a lot to hinge on recompilation. We should leave to the runtime to resolve the virtual call. So not 4. Also, 1 seems too harsh. If you don't call it, why complain? If you call an arbitrary method, that's a little dangerous: you can't reason about what the program does.

Option 2 it is. We can throw. If we realize we were wrong about this later, we can move from there. No bake in.

Is the overriding logic handled at compile time or runtime

Yes

Does the compiler encode what the most specific override is

No

interaction with variance

Can Main be in an interface?

Who cares? Aleksey does. Yes. Because that's the least work.

Static constructors in interfaces?

Probably, but let the runtime folks think about this. Reason is static fields. We allow initializers for static fields, but not an explicit static constructor. Can think about adding later.

In classes you can define virtual prop with private accessor. Allowed in interfaces? What does it mean?

Does an override introduce a member

  • Does it introduce parameter names
  • Does it introduce a new virtual method that could be implemented independently

If the call site sees two separately declared methods, that's already an ambiguity.

interface I1 { void M1(); }

interface I2 : { void M1() { ... } }

interface I3 : I1, I2 { override void M1() { ... } }

I3 x;
x.M1();

Should I3 be allowed? Should x.M1();

Explicit override syntax removes the problem, but do we want to force people to always put the interface in front.

Java's solution does not help us, because independently declared interface members unify.

Xamarin does not expect to need override. They'll just need to know whether there is a default implementation or not. They will need reflection support to query the default methods.

It's not an option to depend on order.

Resolution: I3 is fine, but does not introduce a new member. So the call to M1 is ambiguous. The work-around is to cast to one of the interfaces. However, oddly, you can call it through base(I3).

Parameter names

interface I1 { void M1(int a); }

interface I2 : I1 { override void M1(int b); } // allowed to change parameter names?

I2 x;
x.M(a: 3); // allowed?
x.M(b: 4); // allowed?

The class behavior is to pick the most specific names, but that does mean that introducing an override (with different parameter names) can be a breaking change. But we could issue guidance to not change names in this situation.

From experience, people do intentionally change names on override, but others ask for features to prevent them from doing it!

Common scneario is implementing a generic instance. Going from "T" to Dog.

Let's take the names from the original declaration, deliberately being inconsistent with the class rule for simplicity's sake.

New open question:

what about base(I2) and parameter names?