89 lines
3.8 KiB
Markdown
89 lines
3.8 KiB
Markdown
|
C# Language Design Notes, Nov 15, 2016
|
||
|
======================================
|
||
|
|
||
|
> *Quote of the day*: "Bad people don't get good features!"
|
||
|
|
||
|
|
||
|
Agenda
|
||
|
------
|
||
|
|
||
|
- Tuple name warnings
|
||
|
- "Discards"
|
||
|
|
||
|
|
||
|
Tuple name warnings
|
||
|
===================
|
||
|
|
||
|
There are two kinds of warnings on the table regarding names in tuples:
|
||
|
|
||
|
1. A warning on tuple *literals* only, if the literal uses an element name that isn't in the target type
|
||
|
2. A warning on all expressions of tuple type if an element name in the expression is used for a *different* element in the target type
|
||
|
|
||
|
The first warning helps get the names right when writing a tuple literal, and while maintaining it. It is already in the product:
|
||
|
|
||
|
``` c#
|
||
|
(int x, int y) t = (a: 1, b: 2); // Oops, forgot to update tuple literal? Names a and b would be lost
|
||
|
```
|
||
|
|
||
|
The second warning would help guard against bugs due to reordering of tuple elements:
|
||
|
|
||
|
``` c#
|
||
|
(string firstName, string lastName) GetName();
|
||
|
|
||
|
(string lastName, string firstName) name = GetName(); // Oops, forgot to swap the element names in name?
|
||
|
```
|
||
|
|
||
|
This warning is currently *not* in the product. We would like it to be, but don't have the runway to implement it.
|
||
|
|
||
|
In the future it could be added as an analyzer, or as a compiler warning protected by warning waves (#1580).
|
||
|
|
||
|
|
||
|
"Discards"
|
||
|
==========
|
||
|
|
||
|
We got great feedback on "wildcards" at the MVP Summit, including changing its name to the term "discards", which is spreading in industry.
|
||
|
|
||
|
We are also encouraged to use `_` as the discard character instead of `*`. It works better visually, and is what many other languages already use.
|
||
|
|
||
|
There is a small matter of `_` already being a valid identifier. In order for it to coexist as a discard with existing valid uses we need to use a few tricks. Here are the essential rules:
|
||
|
|
||
|
1. A standalone `_` when no `_` is defined in scope is a discard
|
||
|
2. A "designator" `var _` or `T _` in deconstruction, pattern matching and out vars is a discard
|
||
|
|
||
|
Discards are like unassigned variables, and do not have a value. They can only occur in contexts where they are assigned to.
|
||
|
|
||
|
Examples:
|
||
|
|
||
|
``` c#
|
||
|
M(out _, out var _, out int _); // three out variable discards
|
||
|
(_, var _, int _) = GetCoordinates(); // deconstruction into discards
|
||
|
if (x is var _ && y is int _) { ... } // discards in patterns
|
||
|
```
|
||
|
|
||
|
One use case is to silence warnings when dropping Tasks from async methods:
|
||
|
|
||
|
``` c#
|
||
|
_ = FooAsync();
|
||
|
```
|
||
|
|
||
|
We also want to allow discards as lambda parameters `(_, _) => 0`, but don't expect that to make it in C# 7.0. Today `_` is allowed as an ordinary identifier; the rule would be that it would become a discard if there's more than one.
|
||
|
|
||
|
We can also consider allowing top-level discards in foreach loops:
|
||
|
|
||
|
``` c#
|
||
|
foreach (_ in e) { ... } // I don't care about the values
|
||
|
```
|
||
|
|
||
|
But that does not seem too important, especially since you can use `var _` to the same effect.
|
||
|
|
||
|
In general, whenever use of standalone `_` as a discard is precluded, either by syntactic restrictions or by `_` being declared in the scope, `var _` is often a fine substitute with the same meaning. However, if the declared `_` is a parameter or local, there are situations where you are out of luck:
|
||
|
|
||
|
``` c#
|
||
|
public override void M(int _) // declaring _ to signal that this override doesn't care about it
|
||
|
{
|
||
|
_ = TryFoo(); // Error: cannot assign bool result to int variable _
|
||
|
var _ = TryFoo(); // Error: cannot declare local _ when one is already in scope
|
||
|
}
|
||
|
```
|
||
|
|
||
|
These situations are always local to a member, so it is relatively easy to rewrite them, e.g. to rename the incoming parameter. We could consider accommodating them in the future: the error for a local redeclaration of `_` when one is already in scope could be changed to allowing it, but consider the second declaration a discard. This may be too subtle, and not useful enough, but it is worth considering post C# 7.0.
|