C#14新機能

C#14 新機能 G01_nameofはバインドされていないジェネリック型をサポートします

今回は、C# 14 の新機能「nameof はバインドされていないジェネリック型をサポートします」について解説します。Microsoft Learn の公式ページには次のように説明されています。

図1: Microsoft Learn「C# 14 の新機能」一覧ページ

この一覧の中の「nameof は、バインドされていないジェネリック型をサポートします」の項目を開くと、次のように説明されています。

図2: 「バインドされていないジェネリック型と nameof」の解説ページ

説明を要約すると、「nameof(List<>) は List に評価されます。以前のバージョンでは、List<int> のように何かしらの型を指定する必要がありました」という内容です。今回の変更はごく小さなもので、実務への影響はあまりないと思われますが、内容を確認しておきます。検証用に .NET 10 でプロジェクトを作成します。

nameof のおさらい

そもそも nameof とは何かを簡単におさらいします。nameof は、プロパティ名などの識別子を文字列として取得できる演算子で、保守性やリファクタリング耐性を高めるために使われます。

例えば、ProductName プロパティを持つ Product クラスを用意し、Windows フォーム上でデータバインドする場合を考えます。次のように、画面の Text プロパティに Product インスタンスの ProductName プロパティをバインドします。

public Form1()
{
  InitializeComponent();

  var p = new Product();
  this.DataBindings.Add("Text", p, "ProductName");
}

public class Product
{
  public string ProductName { get; set; } = "AAA";
}

図3: DataBindings.Add で文字列 “ProductName” を指定してバインド

実行すると、画面のタイトルが ProductName プロパティの値である “AAA” になります。

図4: 実行結果。ウィンドウのタイトルが “AAA” になる

文字列指定の欠点

この書き方の欠点は、バインド対象のプロパティ名が “ProductName” という文字列になっている点です。例えば ProductName を ProductNameA にリネームしても、文字列の方は追従して変わらないため、バインドが効かなくなります。しかもこれはコンパイルエラーにならず、実行して初めて気づくバグになりやすいという問題があります。

図5: プロパティ名を変更する前のコード

プロパティ名を ProductNameA に変更し、DataBindings.Add 側の文字列も手動で “ProductNameA” に合わせれば動作は元通りになりますが、この「手動で追従させる」作業自体が保守性の低さの表れです。

図6: プロパティ名と文字列の両方を “ProductNameA” に揃えた状態

nameof を使った改善

そこで、文字列リテラルの代わりに nameof(p.ProductNameA) を使うと、プロパティ名の変更にコンパイラが追従してくれるようになります。プロパティ名を変更すればコンパイルエラーになるため、バグに気づきやすく、保守性が上がります。

this.DataBindings.Add(
"Text", p, nameof(p.ProductNameB));

 

図7: nameof(p.ProductNameB) を使ったバインド指定

バインドされていないジェネリック型のサポート

ここからが今回の本題です。nameof でジェネリック型の名前を取得する場合を考えます。例えば nameof(List<int>) と書くと、取得できるのは “List<int>” ではなく、型パラメーターを含まない “List” という文字列だけです。

var s = nameof(List<int>);

 

図8: nameof(List<int>) と記述

型パラメーターを List<string> に変えても、取得できる文字列はやはり “List” のままです。つまり、型パラメーターに何を指定しても結果には影響しません。

図9: nameof(List<string>) の実行時の値。変数 s の中身は “List”

型パラメーターが結果に影響しないのであれば、そもそも山括弧の中身は空でもよいはずです。これが今回の改良点で、C# 14 からは nameof(List<>) のように型パラメーターを省略して書けるようになりました。

var s = nameof(List<>);

 

図10: nameof(List<>) と記述してもエラーにならない(C# 14)

これも実行時の値は “List” になります。一方、C# 13 以前ではこの書き方はサポートされておらず、コンパイルエラーになります。プロジェクトのターゲット フレームワークを .NET 9 に切り替えて言語バージョンを下げると、次のようにエラーが表示されます。

図11: C# 13 以前でのエラー表示(CS9260)

エラー内容は、「機能 ‘nameof 演算子でバインドされていないジェネリック型’ は C# 13.0 では使用できません。14.0 以上の言語バージョンを使用してください」というものです。C# 14 からはこのエラーが解消され、山括弧の中身を省略して書けるようになった、というのが今回の改良です。

まとめ

nameof(List<int>) や nameof(List<string>) のように、これまでもジェネリック型に対して nameof は使えましたが、型パラメーターに何を指定しても得られる文字列は “List” のみでした。C# 14 では、この型パラメーター部分を省略して nameof(List<>) と書けるようになった、という改良です。

とはいえ、「List<> という書き方で “List” という文字列だけを取りたい」という状況は正直あまり思いつきません。もし実務での具体的な使いどころをご存じの方がいれば、コメントなどで教えていただけると嬉しいです。Microsoft Learn の公式ページでも、この機能についての説明はここで紹介した内容だけで、それ以上の解説はありません。

C#14 新機能