107 lines
3 KiB
Markdown
107 lines
3 KiB
Markdown
# C# Language Design Notes for Mar 21, 2018
|
|
|
|
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
|
|
|
|
## Agenda
|
|
|
|
We discussed various open questions around nullable reference types
|
|
|
|
|
|
# The null-forgiving operator
|
|
|
|
## Name
|
|
|
|
Null-acquiescing
|
|
Null-forgiving
|
|
Null-silencing
|
|
Null-quieting
|
|
Darnit
|
|
|
|
## What does it do?
|
|
|
|
Changes top-level nullability to not-null, as well as silencing warnings even on nested nullability
|
|
|
|
``` c#
|
|
void M(string? ns, List<string?>? ln)
|
|
{
|
|
string s = ns!; // silence and change nullability
|
|
var s2 = ns!; // silence and produce string
|
|
ln = null;
|
|
List<string>? l = ln!; // silence and change
|
|
var l2 = ln!;
|
|
// Idea 1
|
|
l! // change nullability at the top level
|
|
l<!> // idea: change nullability at the nested level
|
|
// Idea 2
|
|
(List<string>?!)ln; // I do a cast that should warn but ! silences it
|
|
List<string>? l3 = (!)ln; // Shorthand
|
|
}
|
|
```
|
|
|
|
Should `!` only change top-level nullability, and not suppress nested diagnostics? Or the other way around? Is it a problem that it does both and "suppresses too much".
|
|
|
|
Since we made affordances for legacy code, the need for passing `null!` for instance is less: unannotated APIs already suppress warnings.
|
|
|
|
Scenarios for `!` now that legacy APIs aren't a scenario:
|
|
|
|
1. I'm in the middle of converting my own code; need them at the boundary
|
|
2. The compiler can't figure it out, but I'm right
|
|
|
|
Great example of the latter:
|
|
|
|
``` c#
|
|
List<string?> strings;
|
|
var query = strings.Where(s => s != null).Select(s => s.Length); // What?? Why warning?
|
|
```
|
|
Here you can put the `!` in `s!.Length`, but what if you have
|
|
|
|
``` c#
|
|
void M(IEnumerable<string> source) { ... }
|
|
List<string?> strings;
|
|
M(strings.Where(s => s != null)); // What?? Why warning?
|
|
```
|
|
|
|
Here you need to suppress on the nested nullability, because you know better.
|
|
|
|
Currently you can say `!` at the end of the argument to set it right. Should that be a different syntax?
|
|
|
|
What's the problem with `!` doing both jobs?
|
|
|
|
``` c#
|
|
void M(string? ns, List<string?>? ln)
|
|
{
|
|
// I happen to know that if ln is not null, it doesn't contain nulls
|
|
List<string>? l = ln!;
|
|
_ = l.Count; // warning?
|
|
}
|
|
```
|
|
|
|
Now `l.Length` is not warned on because `!` did "too much". We wanted it to silence warnings, but it also changed the null state.
|
|
|
|
The simplest fix with current semantics would be to cast to a nullable type:
|
|
|
|
``` c#
|
|
List<string>? l = (List<string>?)ln!; // or
|
|
var l = (List<string>?)ln!;
|
|
```
|
|
|
|
Proposals:
|
|
|
|
1. Change null state and suppress 4
|
|
2. Two syntaxes 5
|
|
3. Only suppress warnings 1
|
|
4. Only change null-state 0
|
|
|
|
Approachable and easy to use. They are all about telling the compiler to shut up when you know what you're doing.
|
|
|
|
For 3, the things we would make people do to change the null state would also be a hurdle.
|
|
|
|
If we separate it, one may be a temporary feature that will go away over time, once people have transitioned.
|
|
|
|
We're not going to settle this today. The prototype does "1+" which is even more lenient than 1.
|
|
|
|
|
|
|
|
# Special methods
|
|
|
|
# Hidden diagnostic |