147 lines
No EOL
5.6 KiB
Markdown
147 lines
No EOL
5.6 KiB
Markdown
# C# Language Design Notes for Mar 21, 2017
|
|
|
|
## Agenda
|
|
|
|
Discussion of default interface member implementations, based on [this guided tour](https://github.com/dotnet/csharplang/issues/288).
|
|
|
|
1. Concerns raised on GitHub and elsewhere
|
|
2. Inheritance?
|
|
3. Breaking name lookup on `this`
|
|
4. Events
|
|
5. Modifiers
|
|
6. Methods
|
|
7. Properties
|
|
8. Overrides
|
|
9. Reabstraction
|
|
10. Most specific override
|
|
11. Static non-virtual members
|
|
12. Accessibility levels
|
|
13. Existing programs
|
|
|
|
|
|
# Concerns raised in various fora
|
|
|
|
- Traits: Not what we're out to address! It may have similar capabilities, but the design point is different, based on different motivations.
|
|
- We are also *not* out to add multiple inheritance to the language - we already have it with interfaces, and we're just making sure that continues to work.
|
|
- It's a bit funny that members of interfaces are inherited into interfaces but not into classes.
|
|
|
|
Interesting design point: This isn't really fully traits, because interface members aren't inherited. The interfaces can't be building blocks for classes.
|
|
|
|
For structs, there aren't really any other ways to factor out behavior.
|
|
|
|
Java did this years ago, and the world did not fall apart.
|
|
|
|
# Inheritance?
|
|
|
|
We could consider letting default-implemented interface members be inherited into classes. That would be a big break away from interfaces today, where an implementing class always has to do *something* to get a corresponding public member. It would also probably make it a breaking change to *add* a default implementation to an existing member, which does not seem attractive.
|
|
|
|
We could have a feature where when you specify a base interface you can ask to have its default implemented members inherited.
|
|
|
|
|
|
# Breaking name lookup on `this`
|
|
|
|
There's a new kind of breaking change that may arise from adding new members to interfaces, where derived interfaces will get name lookup conflicts in their concrete implementations:
|
|
|
|
``` c#
|
|
itf IA { void Foo() {} }
|
|
|
|
itf IB {}
|
|
|
|
itf IC : IA, IB
|
|
{
|
|
void Bar()
|
|
{
|
|
Foo(); // allowed? What if IB adds Foo()?
|
|
this.Foo(); // same? different?
|
|
((IA)this).Foo(); // would be allowed
|
|
this(IA).Foo(); // new syntax?
|
|
}
|
|
}
|
|
```
|
|
|
|
# Events?
|
|
|
|
Absolutely. They aren't materially different. Any member you can declare in an interface should be able to have default implementations.
|
|
|
|
# Modifiers
|
|
|
|
Virtualness is default in interfaces today. We could allow `sealed` to mean non-virtual if you want to override that.
|
|
|
|
Public is default in interface. Should we allow the default to be explicitly stated? All other accessibilities are potentially interesting as well.
|
|
|
|
`extern` scenario: people really like the idea of a scoped PInvoke. People want it for local functions as well.
|
|
|
|
`static`? Yes
|
|
|
|
|
|
Nested types?
|
|
|
|
**Philosophically**, we can either go all (or most of) the way to what classes can do, or do it as sparingly as possible, adding only things that are really needed. Once I can put implementation in an interface, I want all of the other stuff. Moving a method implementation from a class up to an interface shouldn't all of a sudden put a bunch of limitations on how to implement it.
|
|
|
|
Move from "let me have this" to "tell me why I can't have this".
|
|
|
|
|
|
# Methods
|
|
|
|
Can I use plain old `base` (without disambiguation)?
|
|
- Is it of type `object`, and exposes `object` methods?
|
|
- disallow?
|
|
- `base` means "the" base interface, if applicable - may even "mesh" the base interfaces.
|
|
|
|
No `base` for now, just for initial simplicity.
|
|
|
|
|
|
# Properties
|
|
|
|
Abstract properties and events let you implement just one accessor. Can interface properties do that? Our strawman is "yes".
|
|
|
|
# Overrides
|
|
|
|
- Allow explicitly overriding an interface member that you override?
|
|
- Allow implicitly overriding ...?
|
|
|
|
``` c#
|
|
interface I1 { void M(); }
|
|
interface I2 : I1 { override void I1.M() { ... } } // "explicit" override
|
|
interface I3 : I1 { override void M() { ... } } // "implicit" override
|
|
```
|
|
|
|
Allowing the latter means that the `override` keyword is needed, in order to avoid syntactic ambiguity. The latter may override *all* such members? Yes. Direct *and* indirect.
|
|
|
|
There's a bit here about which "class" analogy we apply: the "class inherits class" analogy, or the "class implements interface" analogy. The latter doesn't really apply anyway, so it's more similar to "class inherits class". Therefore we should require the `override` keyword even on `IA.M` explicit member overriding.
|
|
|
|
Properties, can override just one accessor.
|
|
|
|
Tuple names must match, just as between classes. For `dynamic`, same as between classes.
|
|
|
|
|
|
# reabstraction
|
|
|
|
Allow an override to remove implementation. `abstract` keyword is optional.
|
|
|
|
|
|
# Most specific override
|
|
|
|
There has to be a most specific override, otherwise it's an error.
|
|
|
|
Properties and events, the accessors could in principle be treated independently (though there may be a problem with the Roslyn API). Let's instead say that it happens at the whole property level: there must be a most specific override of the property itself.
|
|
|
|
|
|
# static non-virtual members on interfaces
|
|
|
|
Would be useful even without this feature. But a shared helper may also be static.
|
|
|
|
|
|
# all accessibility levels?
|
|
|
|
`public` and `private` for sure. Let's investigate what `internal` and `protected` mean.
|
|
|
|
|
|
# Existing programs
|
|
|
|
We think that example 3 (in this section of the guided tour) is analogous to 2, and should be allowed. However, how can we help someone who thought they were implementing the interface member, but were writing an unrelated private method?
|
|
|
|
Deciding whether a method implicitly implements an interface method is not just matching by signature.
|
|
|
|
We should accept the behavior now, and then look at pitfalls here later.
|
|
|