Add design notes
This commit is contained in:
parent
ed7d0d86b0
commit
325012c654
|
@ -1,57 +1,54 @@
|
|||
# C# Language Design Notes for Apr 19, 2017
|
||||
|
||||
*Quote of the day*: "I'm not thrilled with the decision, but it was a constrained call"
|
||||
|
||||
***Raw notes, yet to be cleaned up - read at your own peril***
|
||||
## Agenda
|
||||
|
||||
1. Improved best common type
|
||||
2. Diamonds with classes
|
||||
3. Structs and default implementations
|
||||
4. Base invocation
|
||||
|
||||
|
||||
"I'm not thrilled with the decision, but it was a constrained call"
|
||||
|
||||
# Improved common type
|
||||
# Improved best common type
|
||||
|
||||
``` c#
|
||||
b ? null : 7 // should be int?
|
||||
b ? null : 7 // should be int? not error
|
||||
```
|
||||
|
||||
This is a small, isolated change that leads to a nice improvement. Let's do it!
|
||||
|
||||
|
||||
# Diamonds with classes
|
||||
|
||||
The class should always win. It's implementation in a class is more specific. "Does the class implement this member? No, then look for a default implementation".
|
||||
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.
|
||||
|
||||
# structs and default implementations
|
||||
|
||||
Very hard to use default implementations from structs.
|
||||
# Structs and default implementations
|
||||
|
||||
Referring to the IB example in the issue.
|
||||
|
||||
Only way:
|
||||
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:
|
||||
|
||||
``` c#
|
||||
public static void Increment<T>(ref T t) where T : IB => t.Increment();
|
||||
public static void Increment<T>(ref T t) where T : I => t.Increment();
|
||||
```
|
||||
|
||||
Even that won't necessarily avoid boxing! What is the type of `this` in the implementation of `IB.Increment`? If it's `IB`, 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 `IB`. But then, if that doesn't have a name, you can't use it in the body. If it does have a name, do we need a syntax for that?
|
||||
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?
|
||||
|
||||
For inherited members on structs today, they do in fact get boxed, but it is never observable - because of the behavior of those three methods (`ToString`, etc.).
|
||||
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.
|
||||
|
||||
|
||||
We can't do 1. - it would undermine the value of the feature. (Because adding a new interface member with a default implementation would break implementing structs).
|
||||
|
||||
Number 2. pushes the problem elsewhere, at a high cost and with a high risk that you box later anyway.
|
||||
|
||||
Number 3. looks like a cop out but there really doesn't seem like a good alternative. C# already has places where structs and interfaces behave weirdly together.
|
||||
|
||||
|
||||
|
||||
Can we make it so that structs *inherit* default interface members? That would almost certainly make it breaking.
|
||||
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.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Let's stick with 3. It would be a breaking change to do 2 later, so we can never change it.
|
||||
Let's stick with option 3. It would be a breaking change to do 2 later, so we can never change it.
|
||||
|
||||
|
||||
# Base invocation
|
||||
|
||||
Like classes it should generate a non-virtual call.
|
||||
What should be the syntax for base invocation? Some candidates:
|
||||
|
||||
``` c#
|
||||
I.base.M()
|
||||
|
@ -59,11 +56,12 @@ base(I).M()
|
|||
base<I>.M()
|
||||
```
|
||||
|
||||
`base(I)` is the winner. It works like `default(T)`.
|
||||
`base(I)` is the winner. It reads like `default(T)`, and seems the best fit.
|
||||
|
||||
Should these be permitted in classes too, to get base interface implementations? Yes.
|
||||
Should these be permitted in classes too, to specify base interface implementations? Yes. Otherwise the diamond problem isn't solved.
|
||||
|
||||
We could actually expand it to classes too, to allow a more distant base implementation to be called. Let's decide that later.
|
||||
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.
|
||||
|
||||
Question: In an interface, can I say `base(IA)` that isn't a *direct* base interface? Yes.
|
||||
Question: In a class, can I say `base(IA)` that isn't a *direct* base interface? Yes.
|
||||
In an interface, can I say `base(IA)` that isn't a *direct* base interface? Yes.
|
||||
|
||||
In a class, can I say `base(IA)` that isn't a *direct* base interface? Yes.
|
|
@ -161,3 +161,11 @@ Design some remaining 7.1 features
|
|||
6. Implicitly implementing non-public interface members
|
||||
7. Not quite implementing a member
|
||||
8. asynchronous `Main`
|
||||
|
||||
## Apr 19, 2017
|
||||
[C# Language Design Notes for Apr 19, 2017](LDM-2017-04-19.md)
|
||||
|
||||
1. Improved best common type
|
||||
2. Diamonds with classes
|
||||
3. Structs and default implementations
|
||||
4. Base invocation
|
||||
|
|
Loading…
Reference in a new issue