78 lines
2.5 KiB
Markdown
78 lines
2.5 KiB
Markdown
# C# Language Design Notes for Feb 21, 2018
|
|
|
|
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
|
|
|
|
|
|
## Agenda
|
|
|
|
Various big and small issues around nullable reference types, preparing for upcoming prototypes
|
|
|
|
|
|
# How are explicit casts interpreted?
|
|
|
|
``` c#
|
|
(object)null // object?, with warning
|
|
(object?)string.Empty // object
|
|
(IEnumerable<object?>)new[]{string.Empty} // IEnumerable<object>
|
|
(IComparer<string?>)Comparer<object>.Default // IComparer<string>, with warning
|
|
```
|
|
|
|
For the top two, there are two approaches you can think of:
|
|
|
|
- nullability should be inferred from the expression
|
|
- the cast represents an intent and we should honor it
|
|
|
|
It's to some degree a tension between existing code or the right design for new code.
|
|
|
|
If we were to infer nullability (rather than take it from the type), would nullability problems always be caught later on?
|
|
|
|
``` c#
|
|
object o = ...;
|
|
var o = (object)...;
|
|
|
|
class X { object[] o; }
|
|
```
|
|
|
|
If we make it more lax, then there's more of a disconnect between top-level nullability and nested nullability.
|
|
|
|
If we think of `?` not so much as a type thing but an observation on what's there, it doesn't seem so onerous to have it tracked locally.
|
|
|
|
Another approach: Have it be a different warning. Then you can switch it off separately, for legacy purposes.
|
|
|
|
For the casts:
|
|
|
|
|
|
``` c#
|
|
(string)null; // A
|
|
M((string)null);// B
|
|
M((string?)x); // C
|
|
M((string?)F()) // D - F is unannotated, and I want to impose my understanding of what it is
|
|
M((string)F()) // E - F is unannotated, and I want to impose my understanding of what it is
|
|
```
|
|
|
|
In C you want to treat the argument as may-be-null, regardless of what is in x.
|
|
In B you could have a warning that is off in legacy code
|
|
|
|
Assume M is generic, and we make type inference based on arguments. Then D and E both make sense - they influence the result type of M, maybe. We shouldn't wave these off, but they are in some sense separable.
|
|
|
|
In the prototype we could give you the choice. We could have it on by default, and the warning could tell you how to turn it off.
|
|
|
|
C is either useless, or it means "forget inferred nullability". And it won't occur in old code.
|
|
|
|
Let's expand B:
|
|
|
|
``` c#
|
|
M((string)y); //
|
|
```
|
|
|
|
Say this is legacy, and now M gets upgraded to take `string?`. And `y` is now possibly null. The warning on `(string)y` could be one of those that is turned off for legacy purposes.
|
|
|
|
Between two options:
|
|
|
|
A: locals are implicitly nullable
|
|
B: locals need explicit annotations like everything else, there's a concession to legacy
|
|
|
|
|
|
|
|
|
|
|