113 lines
No EOL
3.8 KiB
Markdown
113 lines
No EOL
3.8 KiB
Markdown
# 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:
|
|
|
|
``` c#
|
|
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.
|
|
|
|
``` c#
|
|
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
|
|
|
|
``` c#
|
|
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? |