BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ ニュース Build 2018: 将来のC#

Build 2018: 将来のC#

原文(投稿日:2018/05/21)へのリンク

読者の皆様へ:ノイズを減らすための一連の機能を開発しました。関心のあるトピックについて電子メールとWeb通知を受け取ることができます新機能の詳細をご覧ください。

C#の将来機能のリストの中で第一位は、Null許容参照型である。去年の記事で説明したが、簡単に要約すると: 全ての参照変数、パラメーター、フィールドはデフォルトでNull非許容になる。つまり値型のように、Nullを許容したければ、型名にクエスチョンマーク(?)を付けることによって、明示的に指定する必要がある。

これはオプション機能であり、既存のプロジェクトをC# 8にアップグレードしたときには、Null許容参照型は無効になる予定である。新しいプロジェクトでは、Microsoftはデフォルトでこの機能を有効にする考えである。

警告は、潜在的なエラーと、単なる表面上の警告に分類される。例えば、p.MiddleNameがstring?だった場合、以下の行は表面上の警告である。:

string middleName = p.MiddleName;

値が逆参照されるまでは危険ではないため、このローカル変数割り当ては実際に問題ではない。レガシーコードに対する誤検出の数を減らすために警告を無効にできる。

同様に、この機能の以前から存在するライブラリは、パラメータがnullを許可するかどうか知らないため警告をトリガーしない。

null許可参照型のプレビューは、GitHubで提供されている。

Switch式

Switchブロックは、単一の値をシンプルに返すために一般的に使われている。一般的なシナリオでは、構文は対応したものと比べて非常に冗長である。パターンマッチングを使ったこの例を考えてみよう:

static string M(Person person)
{
    switch (person)
    {
        case Professor p:
            return $"Dr. {p.LastName}";
        case Studen s:
            return $"{s.FirstName} {s.LastName} ({s.Level})";
        default:
            return $"{person.FirstName} {person.LastName}";
    }
}

新しい提案では、繰り替えされるcaseとreturnステートメントを削除できる。この結果、より新しい、よりコンパクトな構文にできる。

static string M(Person person)
{
    return person switch
    {
        Professor p => $"Dr. {p.LastName}",
        Student s => $"{s.FirstName} {s.LastName} ({s.Level})",
        _ => $"{person.FirstName} {person.LastName}"
    };
}

実際の構文はまだ議論中である。例えば、この機能が戻り値から分離するパターン式に=>を使うのか:を使うのかがまだ決まっていない。

プロパティパターンマッチング

現在、where句でプロパティレベルのパターンマッチングを実行できる。

case Person p when p.LastName == "Cambell" : return $"{p.FirstName} is not enrolled";

「プロパティパターン」を使うと、こうなる

case Person { LastName: "Cambell"} p : return $"{p.FirstName} is not enrolled";

小さな利点だが、コードをクリアにして、変数名の冗長インスタンスを削減できる。

パターンからプロパティの抽出

さらに進めるとパターン内の変数を宣言することもできる。

case Person { LastName: "Cambell", FirstName: var fn} p : return $"{fn} is not enrolled";

パターンのデコンストラクタ

デコンストラクタは、オブジェクトを構成部品に分解するために使用される。これは主にタプルから複数の代入に使われる。C# 7.3では、パターンマッチングとの分解にも使われる。

以下の例では、Personクラスは{FirstName, MiddleName, LastName}に分解されている。MiddleNameは使っていないため、プロパティをスキップする際のプレースホルダーとして、アンダースコアが使われている。

case Person ( var fn, _, "Cambell" ) p : return $"{fn} is not enrolled";

再帰パターン

次のパターンは、Studentクラスを{FirstName, MiddleName, LastName, Professor}に分解する。Studentオブジェクトとその子Professorオブジェクトを同時に分解できる。

case Student ( var fn, _, "Cambell", var (_, _, ln) ) p : return $"{fn} is enrolled in {ln}’s class";

この一行のコードでは:

  1. student.FirstNameをfnに抽出
  2. student.MiddleNameをスキップ
  3. student.LastNameが“Cambell”に一致
  4. student.Professor.FirstNameとstudent.Professor.MiddleNameをスキップ
  5. student.Professor.LastNameをlnに抽出

再帰パターンのプレビューはGitHubで提供されている。

インデックス式

範囲式(range expression)は、配列のようなデータ構造を処理するための多くの冗長な(エラーが発生しやすい)構文を排除する。

ハット演算子(^)は、リストの最後からカウントする。例えば、文字列の最後の値を取得したい場合は

var lastCharacter = myString[myString.Length-1];

またはシンプルに

var lastCharacter = myString[^1];

これは次のような計算値として動作する

Index nextIndex = ^(x + 1);
var nextChar = myString[nextIndex]

範囲式(Range Expressions)

インデックス式と密接に関連するのが範囲式であり、(..)演算子で示される。これは文字列の最初の3文字を取得するシンプルな例である。

var s = myString.Substring[0..2];

これはインデックス式と組み合わせることができる。次の行では、最初と最後の文字をスキップする。

var s = myString.Substring(1..^1);

これはまたSpan<T>でも動作する。

Span<int> slice = myArray[4…8];

最初の数値は包括され、2番目の数値は排他される。そのため、スライスされるのは4から7の要素である。

インデックスから始まるすべての要素をスライスするためには、以下の2つの選択をできる。

Span<int> rest = myArray[8..];
Span<int> rest = myArray[8..^0];

同様に最初のインデックスを省略することもできる。

Span<int> firstFour = myArray[..4];

不思議に思うかもしれないが、この構文はPythonから強い影響を受けている。C#の大きな違いは、配列の最後からのインデックスに-1を使えないことである。これはすでに.NET配列で意味を持っていたため、代わりに^1構文が使われている。

インデックスと範囲のプレビューはGitHubで提供されている。

IAsyncDisposable

名前が示すように、このインターフェイスはDisposeAsyncメソッドを公開する。これに合わせて、dispose呼び出しにawait機能を追加して、通常のusingブロックのように動作する“using async”という新しい構文が追加されている。

非同期列挙子

IEnumerable<T>のようにIAsyncEnumerable<T>は、長さのわからない有限リストを列挙できる。一致する列挙子は少し違って見える。これは2つのメソッドを公開する:

Task<bool> WaitForNextAsync();
T TryGetNext(out bool success);

このインターフェイスの面白い機能は、データをバッチで読み取ることができることである。バッチで各アイテムに対してTryGetNextを呼び出すことができる。success=falseを返したとき、WaitForNextAsyncを呼び出して、新しいバッチをフェッチできる。

これが重要な理由は、ほとんどのデータがバッチでアプリケーションに送られるか、ネットワーク上でストリームされるからである。TryGetNextを呼び出す時、多くの場合、データはすでに使用可能になっている。Taskオブジェクトを割り当てることは無駄だが、入力バッファのデータが不足した場合でも、非同期で待つことができる。

理想としては、これらのインターフェイスを頻繁に使用しないことである。Microsoftは代わりに、私たちが去年プレビューした非同期イテレータとして知られる“foreach await”構文を使用することをお勧めしている。これは同期と非同期メソッドを必要に応じて処理してくれる。

デフォルトインターフェイスメソッド

このJavaに影響を受けた議論の的になる機能はまだ、C# 8として検討されている。簡単に言えば、これは実装に合わせて新しいインターフェイスを追加することで、インターフェイスを進化させることができる。この方法では新しいメソッドは後方互換を損なわない。

レコード

レコードは素早くimmutableクラスを作成する構文である。私たちは2014年に最初にこれを見た。現在の例は次のようになる:

class Person(string FirstName, string LastName);

基本的にコンストラクタの署名によって完全に定義されたクラスである。immutableで期待される全てのプロパティとメソッドは、自動的に生成される。

 
 

Rate this Article

Adoption Stage
Style
 
 

この記事に星をつける

おすすめ度
スタイル

BT