csharplang/meetings/2017/LDM-2017-04-19.md

67 lines
3 KiB
Markdown
Raw Normal View History

2017-04-20 02:14:41 +02:00
# C# Language Design Notes for Apr 19, 2017
2017-06-02 02:50:56 +02:00
*Quote of the day*: "I'm not thrilled with the decision, but it was a constrained call"
2017-04-20 02:14:41 +02:00
2017-06-02 02:50:56 +02:00
## Agenda
2017-04-20 02:14:41 +02:00
2017-06-02 02:50:56 +02:00
1. Improved best common type
2. Diamonds with classes
3. Structs and default implementations
4. Base invocation
2017-04-20 02:14:41 +02:00
2017-06-02 02:50:56 +02:00
# Improved best common type
2017-04-20 02:14:41 +02:00
``` c#
2017-06-02 02:50:56 +02:00
b ? null : 7 // should be int? not error
2017-04-20 02:14:41 +02:00
```
This is a small, isolated change that leads to a nice improvement. Let's do it!
2017-06-02 02:50:56 +02:00
# Diamonds with classes
2017-04-20 02:14:41 +02:00
2017-06-02 02:50:56 +02:00
A class implementation of an interface member should always win over a default implementation in an interface, even if it is inherited from a base class. Default implementations are always a fallback only for when the class does not have any implementation of the member at all.
2017-04-20 02:14:41 +02:00
2017-06-02 02:50:56 +02:00
# Structs and default implementations
2017-04-20 02:14:41 +02:00
2017-06-02 02:50:56 +02:00
It is very hard to use default implementations from structs without boxing. In general, the only way to use interface methods on a struct without copying or boxing is through generics and ref parameters:
2017-04-20 02:14:41 +02:00
``` c#
2017-06-02 02:50:56 +02:00
public static void Increment<T>(ref T t) where T : I => t.Increment();
2017-04-20 02:14:41 +02:00
```
2017-06-02 02:50:56 +02:00
But for default implementations, even that won't necessarily avoid boxing! What is the type of `this` in the default implementation of `I.Increment`? If it's `I`, then `this` is a boxed form of the struct! We can avoid that by having an implicit type parameter, a "this type", which is constrained by `I`. But then, what is the name of that implicit type parameter in the body? If it doesn't have a name, you can't use it in the body. If it does have a name, what is the syntax for that?
2017-04-20 02:14:41 +02:00
2017-06-02 02:50:56 +02:00
For inherited members on structs today, they do in fact get boxed, but it is never observable - because the behavior of those three methods (`ToString`, etc.) doesn't mutate, or otherwise reveal the boxing.
2017-04-20 02:14:41 +02:00
2017-06-02 02:50:56 +02:00
What can we do?
1. *Forbid structs to make use of default implementations*: Can't do that. Now adding a new interface member with a default implementation would break implementing structs.
2. *Come up with some code gen strategy like implicit this types*: Pushes the problem elsewhere, at a high cost and with a high risk that you box later anyway.
3. *Don't worry about it, and leave it as a wart in the language*: It looks like a cop out, but there really doesn't seem to be a good alternative. C# already has places where structs and interfaces behave weirdly together; this just adds to the pile.
2017-04-20 02:14:41 +02:00
## Conclusion
2017-06-02 02:50:56 +02:00
Let's stick with option 3. It would be a breaking change to do 2 later, so we can never change it.
2017-04-20 02:14:41 +02:00
# Base invocation
2017-06-02 02:50:56 +02:00
What should be the syntax for base invocation? Some candidates:
2017-04-20 02:14:41 +02:00
``` c#
I.base.M()
base(I).M()
base<I>.M()
```
2017-06-02 02:50:56 +02:00
`base(I)` is the winner. It reads like `default(T)`, and seems the best fit.
Should these be permitted in classes too, to specify base interface implementations? Yes. Otherwise the diamond problem isn't solved.
2017-04-20 02:14:41 +02:00
2017-06-02 02:50:56 +02:00
We could actually expand it to work *on* classes too, to allow a more distant base implementation to be called. Let's decide that later.
2017-04-20 02:14:41 +02:00
2017-06-02 02:50:56 +02:00
In an interface, can I say `base(IA)` that isn't a *direct* base interface? Yes.
2017-04-20 02:14:41 +02:00
2017-06-02 02:50:56 +02:00
In a class, can I say `base(IA)` that isn't a *direct* base interface? Yes.