目次

Ver. 10.0

※ 2021/7/24 現在、プレビュー版です。

リリース時期 2021/?
同世代技術
  • .NET 6.0
  • Visual Studio 2022
要約・目玉機能

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

record struct

C# 9.0 (レコード型の最初のバージョン)では、レコード型は常に参照型(クラスと同系統の型)になります。 これに対して C# 10.0 では値型も選べるようにしました。 そのため、以下のように、record classrecord struct というキーワードで書き分けができるようになりました。

record class Reference(int X, int Y); // record だけ書いた場合こちらと同じ意味
record struct Value(int X, int Y);

詳しくは 「レコード型」のページ内に色々と追記しました。

構造体の引数なしコンストラクター

構造体に引数なしコンストラクターとかフィールド初期化子を書けるようになりました。

struct A
{
    public int X;
    public A() => X = 1;
}

これで、new A()X が1になります。 詳しくは「引数なしコンストラクター」で説明します。

文字列補間

文字列補間に2点ほど改善が入りました。

パフォーマンス改善

string.Format を使った実装ではどうしてもパフォーマンス上の改善が難しく、 別の型を使って結構複雑なコードに変換する最適化が入りました。 条件を満たす場合、

var formatted = $"({x}, {y})";

このコードは string.Format ではなく、以下のようなコードに展開されます。

DefaultInterpolatedStringHandler handler = new DefaultInterpolatedStringHandler(4, 2);
handler.AppendLiteral("(");
handler.AppendFormatted(x);
handler.AppendLiteral(", ");
handler.AppendFormatted(y);
handler.AppendLiteral(")");
string s = handler.ToStringAndClear();

詳しくは「C# 10.0 の補間文字列の改善」で説明します。

const 文字列補間

文字列補間でも、{} の中身が const 文字列な場合に限り、補完結果も const にできます。 例えば以下のような const 文字列を作れます。

const string A = "Abc";
const string B = "Xyz";
const string C = $"{nameof(A)}: {A}, {nameof(B)}: {B}"; // "A: Abc, B: Xyz"

詳しくは「const 文字列補間」で説明します。

CallerArgumentExpression 属性

CallerArgumentExpression 属性を使って、メソッド呼び出し元でどの引数にどういう式を渡したかを文字列として取れるようになりました。

using System.Runtime.CompilerServices;

m(2 * 3 * 5);

static void m(
    int x,
    [CallerArgumentExpression("x")] string? expression = null)
{
    Console.WriteLine($"{expression} = {x}");
}
2 * 3 * 5 = 30

詳しくは「呼び出し元情報(caller info)」で説明します。

シンプル プログラム

C# 9.0 のトップ レベル ステートメントに続いて、シンプルなプログラムであればシンプルなソースコードで書けるようになる機能が増えています。

これらの機能によって、いわゆる Hello World プログラムを以下の1行で書けるようになりました。

Console.WriteLine("Hello, World!");

実際、 .NET 6 からはコンソール アプリのプロジェクト テンプレートがこの1ファイル、1行だけのものになっています。

参考: 「最初の C# プログラム

ファイル スコープ名前空間

C# 10.0 から {} なしの以下のような書き方で名前空間を指定できるようになりました。

namespace Namespace;

class A { }

これで以下のコードと同じ意味になります。

namespace Namespace
{
    class A { }
}

詳しくは「ファイル スコープ namespace」で説明します。

global using

using ディレクティブの前に global という修飾を付けることで、 プロジェクト内全域に対して影響を及ぼす using (名前空間の参照)ができるようになりました。

例えば、プロジェクト内のどこか1つのファイルに以下のようなコードを書いたとします。

global using System;

これで、このプロジェクト内のすべてのファイルで、ファイルの先頭に using System; を書いたのと同じ状態になります。

詳しくは「global using」で説明します。

その他

プロパティ パターンの拡張(入れ子のメンバー参照)

入れ子のプロパティ・フィールド参照でプロパティ パターンを書けるようになりました。

    if (x is { Name.Length: 1 })
    {
        Console.WriteLine("single-char Name");
    }

詳しくは「プロパティ パターン」で説明します。

分解宣言と分解代入の混在

分解代入と分解宣言の混在もできるようになりました。

int x;
(x, var u) = (1, 2);

ただし、式の途中に分解宣言 (var 付きの宣言) が来るようなコードは C# 10.0 でも書けません。

int x, y;
(x, var u) = (var v, y) = (1, 2);

更新履歴

ブログ