1 年ほど前 C# 7.0 の新規のについていろいろ書いていたのですが、値の破棄 (discards) という機能について書いてなかったことに気が付きました!ということで、イマサラですが紹介します。
これまでの「値を使わない」ときの書き方
コンパイルを通すために引数を指定しなければならないけれど、その引数は今使いません。
…と言ったケースはままあります。そんな「値を無視したい」ときによく見かける実装が「_ (アンダースコア)」変数によるエスケープです。慣例レベルではありますが、「利用しない変数」であることを明示した書き方をします。例えば以下のような感じです。
int _;
if (int.TryParse("123", out _))
{}
しかし C# においてアンダースコアは変数名として有効なので、複数の値を無視したい場合は以下のようにしなければなりません。
int _;
if (int.TryParse("123", out _)){}
int __;
if (int.TryParse("456", out __)){}
int ___;
if (int.TryParse("789", out ___)){}
___ += 10;
Console.WriteLine(___);
「値の破棄」を明示したい
_
の数がドンドン増えるのはイヤだ
- 値を無視するという意図の変数を誤って使わせたくない
そんなお気持ちを C# 7.0 が「discards (値の破棄)」としてサポートします。後述しますが、以下のようにいくつかのシチュエーションでのみアンダースコアが特別扱いを受けます。
private void OutVariable(out int value)
=> value = 123;
this.OutVariable(out var _);
this.OutVariable(out _);
_ += 10;
実は out var _
のように型を書く必要はなく out _
だけでも OK です。また上記の場合 _
は変数として認められていないため、以下のような書き方をしてもコンパイルエラーになりません。
private void OutVariable2(out int x, out string y)
{
x = 123;
y = "abc";
}
this.OutVariable2(out var _, out var _);
this.OutVariable2(out _, out _);
discards が利用できる箇所
ザッと調べた範囲では、現状の C# 7.0 では以下の箇所で値の破棄の構文を使うことができます。
var (name, _) = ("xin9le", 32);
var (_, _) = ("xin9le", 32);
switch ("abc")
{
case string _:
break;
}
LINQ やイベントハンドラを記述する際のラムダ式でも頻繁に _
を使うことがありますが、C# 7.0 ではサポートされていません。これは今後のバージョンに期待…かもしれません。
逆コンパイル
この機能がどうやって実現されているのか、いつも通り逆コンパイルしてのぞいてみます。すると、以下のような C# 6 までのフツーのコードに展開されます。
this.OutVariable2(out _, out _);
int item1;
string item2;
this.OutVariable2(out item1, out item2);
つまり discards の構文であることを C# コンパイラが上手に判断して、良きに計らってくれているということですね。IL レベルでは一切変更がありません。