C# 10 の新機能

C# 10 によって、C# 言語に次の機能と機能強化が追加されています。

"プレビュー" モードでは、追加の機能を使用できます。 これらの機能を試していただき、それに関するフィードバックを是非お送りください。 これらは、最終的なリリースの前に変更される可能性があります。 これらの機能を使用するには、プロジェクトで <LangVersion>Preview に設定する必要があります。 汎用属性の詳細については、この記事の後半で説明します。

C# 10 は .NET 6 でサポートされています。 詳細については、「C# 言語のバージョン管理」を参照してください。

最新の .NET 6 SDK は .NET のダウンロード ページからダウンロードできます。 .NET 6 SDK を含む Visual Studio 2022 プレビューをダウンロードすることもできます。

レコード構造体

record struct または readonly record struct 宣言を使用して、値型レコードを宣言できます。 record class 宣言を使用して、record が参照型であることを明確にできるようになりました。

構造体型の機能強化

C# 10 では、構造体型に関連する次の機能強化が導入されています。

  • 構造体型でインスタンスのパラメーターなしのコンストラクターを宣言し、その宣言でインスタンス フィールドまたはプロパティを初期化することができます。 詳細については、構造体型に関する記事の「パラメーターなしのコンストラクターとフィールド初期化子」セクションを参照してください。
  • withの左側のオペランドは、任意の構造体型にすることも、匿名 (参照) 型にすることもできます。

補間された文字列ハンドラー

補間された文字列式から結果の文字列を作成する型を作成できます。 .NET ライブラリでは、多くの API でこの機能が使用されています。 こちらのチュートリアルに従って構築することができます。

グローバルな using ディレクティブ

global 修飾子を任意の using ディレクティブに追加すれば、コンパイル時にディレクティブをすべてのソース ファイルに適用するようにコンパイラに指示できます。 これに該当するのは、通常、プロジェクト内のすべてのソース ファイルです。

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

新しい形式の namespace 宣言を使用することで、後続のすべての宣言が宣言された名前空間のメンバーであることを宣言できます。

namespace MyNamespace;

この新しい構文では、宣言の水平方向と垂直方向の両方の領域 namespace が保存されます。

拡張プロパティのパターン

C# 10 以降、入れ子になったプロパティまたはプロパティ パターン内のフィールドを参照できます。 たとえば、フォームのパターン

{ Prop1.Prop2: pattern }

は、C# 10 以降で有効で、以下と同じです

{ Prop1: { Prop2: pattern } }

これは、C# 8.0 以降で有効です。

詳細については、機能提案ノートの「拡張プロパティ パターン」を参照してください。 プロパティ パターンの詳細については、「パターン」の記事の「プロパティ パターン」セクションを参照してください。

ラムダ式の機能強化

C# 10 には、ラムダ式の処理方法に多くの機能強化が加えられています。

  • ラムダ式には自然型があり、コンパイラによってラムダ式またはメソッド グループからデリゲート型を推測できます。
  • ラムダ式には、コンパイラによって推測できない場合に、戻り値の型を宣言できます。
  • 属性はラムダ式に適用できます。

このような機能があるため、ラムダ式はメソッドとローカル関数に似ています。 これにより、デリゲート型の変数を宣言することなく、ラムダ式を簡単に使用できます。また、新しい ASP.NET Core Minimal API とよりシームレスに連携します。

定数の補間文字列

C# 10 では、すべてのプレースホルダー自体が定数文字列の場合、文字列補間を使用して const 文字列を初期化できます。 文字列補間では、アプリケーションで使用される定数文字列を構築するときに、より読みやすい定数文字列を作成できます。 プレースホルダー式は、実行時にそれらの定数が文字列に変換されるため、数値定数にすることはできません。 現在のカルチャは、それらの文字列形式に影響を与える可能性があります。 詳細については、言語リファレンスの constに関するページを参照してください。

レコードの型で ToString を封印することができる

C# 10 では、レコードの型で ToString をオーバーライドするときに sealed 修飾子を追加できます。 ToString メソッドを封印すると、コンパイラで派生レコード型に対して ToString メソッドを合成できなくなります。 sealed ToString により、すべての派生レコード型で共通の基本レコード型で定義された ToString メソッドが確実に使用されるようになります。 この機能の詳細については、この記事のレコードに関する記述を参照してください。

同じ分解内の代入と宣言

この変更により、以前のバージョンの C# からの制限がなくなります。 以前は、分解ですべての値を既存の変数に代入したり、新しく宣言された変数を初期化したりすることができました。

// Initialization:
(int x, int y) = point;

// assignment:
int x1 = 0;
int y1 = 0;
(x1, y1) = point;

C# 10 では、この制限がなくなります。

int x = 0;
(x, int y) = point;

限定代入の機能強化

C# 10 より前のバージョンでは、限定代入と null 状態分析によって擬陽性である警告が生成されるシナリオが多くありました。 通常、これらには、ブール値定数との比較、if ステートメント内の true または false ステートメントでのみ変数にアクセスすること、null 合体式が含まれていました。 これらの例では、以前のバージョンの C# では警告が生成されていましたが、C# 10 では生成されません。

string representation = "N/A";
if ((c != null && c.GetDependentValue(out object obj)) == true)
{
   representation = obj.ToString(); // undesired error
}

// Or, using ?.
if (c?.GetDependentValue(out object obj) == true)
{
   representation = obj.ToString(); // undesired error
}

// Or, using ??
if (c?.GetDependentValue(out object obj) ?? false)
{
   representation = obj.ToString(); // undesired error
}

この機能強化の主な影響は、限定代入と null 状態分析の警告がより正確になったことです。

メソッドで AsyncMethodBuilder 属性を許可する

C# 10 以降では、特定のタスクのような型を返すすべてのメソッドに対してメソッド ビルダー型を指定するだけでなく、1 つのメソッドに対して別の非同期メソッド ビルダーを指定することができます。 カスタム非同期メソッド ビルダーにより、特定のメソッドでカスタム ビルダーのベネフィットが得られる高度なパフォーマンス チューニング シナリオが可能になります。

詳細については、コンパイラによって読み取られる属性についての記事の AsyncMethodBuilder に関するセクションを参照してください。

CallerArgumentExpression 属性の診断

System.Runtime.CompilerServices.CallerArgumentExpressionAttribute を使って、コンパイラによって別の引数のテキスト表現に置き換えられるパラメーターを指定できます。 この機能を使うと、ライブラリからより具体的な診断を作成できるようになります。 次のコードを使うと、条件をテストできます。 条件が false の場合、例外メッセージには condition に渡された引数のテキスト表現が含まれています。

public static void Validate(bool condition, [CallerArgumentExpression("condition")] string? message=null)
{
    if (!condition)
    {
        throw new InvalidOperationException($"Argument failed validation: <{message}>");
    }
}

この機能の詳細については、呼び出し元情報の属性に関する記事の言語リファレンス セクションを参照してください。

拡張 #line pragma

C# 10 は、#line pragma の新しい形式をサポートしています。 この新しい形式を使わない可能性もありますが、その効果はわかるはずです。 この機能強化により、Razor などのドメイン固有の言語 (DSL) で、よりきめ細かい出力が可能になります。 Razor エンジンによってこれらの拡張機能が使われ、デバッグ エクスペリエンスが向上します。 デバッガーで Razor ソースをより正確に強調表示できることがわかります。 新しい構文の詳細については、言語リファレンスのプリプロセッサ ディレクティブに関する記事を参照してください。 また、Razor ベースの例については、機能仕様に関する記事を参照してください。

汎用属性

重要

"汎用属性" はプレビュー機能です。 この機能を有効にするには、<LangVersion>Preview に設定する必要があります。 この機能は、最終リリース前に変更される可能性があります。

基底クラスが System.Attribute である汎用クラスを宣言できます。 これにより、System.Type パラメーターを必要とする属性の構文がより便利になります。 以前は、Type をコンストラクター パラメーターとして受け取る属性を作成する必要がありました。

public class TypeAttribute : Attribute
{
   public TypeAttribute(Type t) => ParamType = t;

   public Type ParamType { get; }
}

属性を適用するには、typeof 演算子を使用します。

[TypeAttribute(typeof(string))] 
public string Method() => default;

この新機能を使用して、代わりに、次のように汎用属性を作成することができます。

public class GenericAttribute<T> : Attribute { }

次に、属性を使用する型パラメーターを指定します。

[GenericAttribute<string>()]
public string Method() => default;

完全に閉じた構築の汎用属性を適用できます。 つまり、すべての型パラメーターを指定する必要があります。 たとえば、以下は許可されません。

public class GenericType<T>
{
   [GenericAttribute<T>()] // Not allowed! generic attributes must be fully closed types.
   public string Method() => default;
}

型引数は、typeof 演算子と同じ制限を満たす必要があります。 メタデータ注釈が必要な型は許可されません。 具体的には次のものがあります。

  • dynamic
  • nint, nuint
  • string? (または Null 許容参照型)
  • (int X, int Y) (または C# タプル構文を使用するその他のタプル型)。

これらの型は、メタデータで直接表現されません。 型を記述する注釈が含まれています。 すべてのケースで、代わりに基になる型を使用できます。

  • objectdynamic
  • nint または unint の代わりに IntPtr
  • string (string? の代わり)
  • ValueTuple<int, int> ((int X, int Y) の代わり)