csharplang/meetings/2017/LDM-2017-05-17.md
2017-06-19 17:05:19 -07:00

3.8 KiB

C# Language Design Notes for May 17, 2017

Agenda

More questions about default interface member implementations

  1. Conflicting override of default implementations
  2. Can the Main entry point method be in an interface?
  3. Static constructors in interfaces?
  4. Virtual properties with private accessors
  5. Does an override introduce a member?
  6. Parameter names

Conflicting override of default implementations

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 without compiler complaint 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 one, 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.

Conclusion

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

Can the Main entry point method be in an interface?

Yes. No good reason why not. Also, that's the least work.

Static constructors in interfaces?

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

Virtual properties with private accessors

In classes you can define a virtual property with private accessor. Allowed in interfaces? What does it mean? Probably disallow.

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? Does it override both? 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 in Java.

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.

Conclusion

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 scenario is implementing a generic instance. Going from T to Dog.

Conclusion

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?