目次

Ver. 9.0
リリース時期 未定(おそらく .NET 5 ど同じ 2020/11)
同世代技術
  • .NET 5
要約・目玉機能

.NET 5 Preview 2 (2020/3)頃から、C# 9.0 機能もちらほらプレビュー実装され始めました。 プレビュー状態なので言語バージョンpreview の指定が必要です。

執筆予定: C# 9.0 トラッキング issue

トップ レベル ステートメント

トップ レベル(top-leve: クラスや名前空間よりも外側、ファイル直下)にステートメントを直接書けるようになりました。

例えばよくある「Hello World」であれば、単に以下のように書けるようになります。

using System;
Console.WriteLine("Hello World!");

詳しくは「トップ レベル ステートメント」で説明します。

ターゲットからの new 型推論

ターゲット型からの推論が効く場合に、new T()T の部分を省略できるようになりました。 (target-typed new とか呼ばれたりします。)

特に、var が使えず、 型名が長い特に便利です。

using System.Collections.Generic;
 
class Sample
{
    // フィールドに対しては var が使えない。
    // 代わりに new 型推論を使うと便利なことがある(特に、型名が長い時)。
    Dictionary<string, List<(int x, int y)>> _cache = new();
}

詳しくは「ターゲットからの new 型推論」で説明します。

unsafe/ネイティブ相互運用向け機能

C# 7.2の辺りから、 言語の方向性として生産性や安全性を優先する C# でも、 パフォーマンス改善を目的とするような言語機能が結構増えてきました。

また、 クロスプラットフォーム化が進んだことで、ネイティブ相互運用関連の機能も増えています。

この手の機能は一般的な開発者が直接触れることは少ないですが、 .NET ランタイム自体や、大規模に使われているライブラリのパフォーマンス改善につながり、 間接的にすべての C# 開発者が恩恵を受けるものになります。

ローカル変数の0初期化抑止

/unsafe オプション指定時限定ですが、ローカル変数の0初期化を抑止できるようになりました。

using System;
using System.Runtime.CompilerServices;
using System.Text.Unicode;
 
m("aあ😀");
 
// この属性を付けると stackalloc の要素の0初期化がなくなる。
[SkipLocalsInit]
static void m(string s)
{
    // UTF-16 の文字数に大して、UTF-8 のバイト数は最大でも3倍以内。
    Span<byte> buffer = stackalloc byte[s.Length * 3];
    Utf8.FromUtf16(s, buffer, out _, out var bytesWritten);
 
    // FromUtf16 の仕様上、bytesWritten バイト目までは必ず上書きされる。
    // 上書きされた部分だけを使う分には0初期化は「余計なお世話」。
    var written = buffer[..bytesWritten];
 
    foreach (var b in written)
    {
        Console.WriteLine(b);
    }
}

詳しくは「ローカル変数の0初期化抑止」で説明します。

その他

ラムダ式の引数を破棄

ラムダ式の引数で、_ を使った値の破棄ができるようになりました。

static void Subscribe(INotifyPropertyChanged source)
{
    // _ を破棄扱いして、2個以上並べられる
    source.PropertyChanged += (_, _) => { };
}

詳細は「値の破棄 - ラムダ式の引数」で説明します。

ローカル関数への属性適用

ローカル関数に属性を付けられるようになりました。

using System;
using System.Diagnostics.CodeAnalysis;
 
m("", "");
 
static void m(string? a, string? b)
{
    // C# 9.0 からローカル関数に属性を付けれる。
    // C# 8.0 の null 許容参照型がらみで特に有用。
    [return: NotNullIfNotNull("s")]
    string? toLower(string? s) => s?.ToLower();
 
    if (a is not null && b is not null)
    {
        // a, b の null 許容性が、NotNullIfNotNull 属性のおかげで al, bl に伝搬。
        string al = toLower(a);
        string bl = toLower(a);
 
        // a, b が非 null なので、al, bl は非 null で確定済み。改めてのチェック不要。
        Console.WriteLine(al.GetHashCode());
        Console.WriteLine(bl.GetHashCode());
    }
}

更新履歴

ブログ