C# 14の新しいfieldキーワード
[[academy-video-youtube({"vid": "_Z551_SKHA4", "start_time": "0", "title": "C# 14 における新しいフィールドキーワード", "creator": "Tim Corey", "length": "10m 35s"})]]
C#の自動プロパティは簡素ですが、セッターに検証や変換ロジックが必要になるとすぐに、完全なプロパティを書き、手動でバッキングフィールドを使用しなければならなくなります。 一行から七行へのジャンプは、単一のガード句を追加するための急な税金です。 C# 14 では、field キーワードが導入され、このギャップを埋めることで、バックフィールドをコンパイラが管理しながら、ゲッターやセッターをカスタマイズできるようにします。
彼のビデオで"C# 14の新しいフィールドキーワード"Tim Coreyは、特徴的な問題を解決し、セッター検証の実用的な例を紹介し、アップグレードする前に知っておくべき名前競合について説明します。 各ステップを詳細に追いながら、field を自分のプロパティで自信をもって使い始めることができるようにします。
セットアップ: シンプルな人物モデル
[0:12 - 1:07] Timは、.NET 10とVisual Studio 2026で動作するコンソールアプリケーションから始めます。デモは、いくつかのプロパティを持つPersonクラスを中心に進行します。
public required string FirstName { get; set; }
public required string LastName { get; set; }
public int Age { get; set; }public required string FirstName { get; set; }
public required string LastName { get; set; }
public int Age { get; set; }命名の衝突が表面化すると関連性が出てくるプライベートフィールドでバックアップされたDemoプロパティもあります。 Program.cs では、TimはFirstName = "Tim" とLastName = "Corey"を使ってインスタンスを作成し、その後、姓、年齢、デモの値を出力します。 すべてが期待どおりに出力されます: "Corey", 0 (デフォルトの整数)、および "テスト"。
問題: 自動プロパティは不正なデータを受け入れる
[1:23 - 2:49] 問題は、コンストラクション後にTimがnullを代入するときに表面化します。
p.LastName = null;p.LastName = null;requiredでマークされ、非ヌル文字列として型付けされていても、代入はコンパイルされます。 required 修飾子は、オブジェクト初期化時に値が提供されることを強制するにすぎません; それは、後で誰かがプロパティにnullを設定するのを防ぐものではありません。 その結果、ランタイムでエラーが発生せずに姓が空白になります。
これはデータの完全性における実際のギャップです。 型システムはnullable参照スキグルで警告しますが、それはコンパイルタイムのヒントであり、ランタイムのガードではありません。 アプリケーションがLastNameに常に有効な文字列が含まれることに依存している場合、自動プロパティだけではその契約を強制することはできません。
従来の修正: 手動バッキングフィールドを持つ完全なプロパティ
[2:58 - 4:19] C# 14以前、標準的な解決策は自動プロパティを手動のバッキングフィールドを持つ完全なプロパティに変換することでした:
private string _lastName;
public required string LastName
{
get => _lastName;
set => _lastName = value ?? throw new ArgumentNullException(nameof(LastName));
}private string _lastName;
public required string LastName
{
get => _lastName;
set => _lastName = value ?? throw new ArgumentNullException(nameof(LastName));
}Timさんがこれを実行し、例外が正しく発生することを確認します: "値はnullにできません。 パラメータ名: LastName." このアプローチは機能しますが、プライベートフィールドの宣言、ゲッターとセッターの結線、プロパティ名を複数行にわたって繰り返す必要があります。 単一の検証ルールのためには多くの手間です。
この場合、ゲッターは何も特別なことをしません。" それはフィールドを変更せずに返します。 それでも、自動プロパティの領域を離れるときには、構文が両側を書くことを求めるため、明示的に書く必要があります。 Timさんがこの冗長さを新しい機能の動機と捉えています。
C# 14 ソリューション: フィールド キーワード
[4:23 - 5:47] C# 14は中間の道を導入します。 あなた自身でプライベートなバックフィールドを宣言する代わりに、ゲッターまたはセッター内でコンテクスチュアルキーワードfieldを使用して、コンパイラが生成したバックフィールドを直接参照します。
public required string LastName
{
get;
set => field = value ?? throw new ArgumentNullException(nameof(LastName));
}public required string LastName
{
get;
set => field = value ?? throw new ArgumentNullException(nameof(LastName));
}ゲッターは、get; 自動実装のままにしておき、ボディを必要としません。 セッターは、バリデーション後にインカミングfieldを使用します。 コンパイラは、標準の自動プロパティと同様に、舞台裏でバッキングフィールドを作成し、管理します。
デモを実行すると、ヌル代入時に同じArgumentNullExceptionが生成されます。 動作は、手動でバックされたバージョンと同一であり、7行から、必要なものだけをカスタマイズした集中的なブロックに圧縮されます。 あなたは自動プロパティゲッターを維持し、セッターだけに論理を追加し、手動のフィールド宣言を完全にスキップします。
これは、プレーンな自動プロパティ(一行、検証なし)と完全なプロパティ(7行以上、完全な管理)との間の有用な中間ステップを提供します。 あなたのロジックがセッターだけに影響を与える場合、ゲッターも再記述するという文法的なコストを払う必要はありません。
セッターガードを用いた年齢の検証
[6:16 - 7:39] Age プロパティに範囲バリデーションを追加します。
public int Age
{
get;
set
{
if (value > 0 && value < 120)
field = value;
}
}public int Age
{
get;
set
{
if (value > 0 && value < 120)
field = value;
}
}ここで、セッターは範囲外の値を黙って無視します。 Ageはデフォルトのゼロのままです。 Timは例外をスローすることもできると述べていますが、沈黙のアプローチは、セッターボディがストレージにfieldを依存しながら任意のロジックを含むことができることを示しています。
パターンは広く適用されます: 数値の範囲のクランプ、文字列のホワイトスペースのトリミング、大小の正規化、またはプロパティが設定されるたびに適用されたい変換。
既存のフィールド変数との名前の衝突
[7:39 - 9:43] Timさんは意図的なエッジケースを紹介します。 デモクラスは、文字通りfieldという名前のプライベートメンバーを持っています。
private string field = "test";private string field = "test";C# 14が有効になったとき、コンパイラはプロパティアクセサ内のfieldを変数ではなくキーワードと見なします。 つまり、プロパティがfieldを参照すると、プロパティの背後に隠されたストレージ(空です)からサイレントに読み取り、"テスト"を含む文字列メンバーではなくなります。 出力は誤りを指摘しているものはなく、警告のみを伴って空白に変更されます。
2つの回避方法が存在します。this.fieldをプレフィックスすることで、コンパイラにキーワードではなくクラスレベルのメンバーを意味していることを伝えます。 または、@fieldエスケープを利用することで同様に動作します。
// Both refer to the instance variable, not the keyword
string demo => this.field;
string demo => @field;// Both refer to the instance variable, not the keyword
string demo => this.field;
string demo => @field;Timの強い推奨は、C# 14にアップグレードする際にfieldと呼ばれる変数をすべてリネームすることです。IDEでのクイック"すべてのリネーム"で曖昧さを永久に除去します。 競合はプロパティアクセッサ内でのみ発生します; コンストラクターとメソッドは、期待通りにfieldを変数名として解決します。これらのコンテキストには暗黙のバックストアがないからです。
まとめ: ボイラープレート削減、同じ制御
[10:04 - 10:28] fieldキーワードは、日常のC#コードにおける実践的なギャップを埋めます。 一つのガードクローズまたは変換を必要とするプロパティは、もはや手動でバッキングフィールドを追加して完全に再書く必要はありません。 あなたは論理が必要なアクセッサのみをカスタマイズし、他のものは標準の自動実装として残します。
結論
[10:28 - 10:35] 要約すると、C# 14のfieldキーワードは、任意のプロパティアクセサ内で暗黙のバックストアに直接アクセスできるようにします。 それを使ってセッターの検証、ゲッターの変換、または両方を追加し、カスタマイズが必要ない部分のために自動プロパティ構文を放棄しません。
アップグレードする前に、コードベースを検索して、fieldという名前の変数をすべて見つけてリネームしてください。 この特徴が引き起こす唯一の注意点を回避するための唯一の優れた予防策です。 それを超えて、これはほとんどの開発者が既にモデルを構造化する方法に自然に合致するボイラープレートのクリーンな削減です。
例のヒント: セッターだけを検証する必要がある場合、ゲッターはボディのないプレーンなget;としてそのままにしておきます。 コンパイラはそれを自動プロパティのゲッターとして処理し、何も追加しない透過的なreturn文を書くのを避けます。

