「C# オートプロパティの裏側 ― コンパイラが生成する バッキングフィールド を覗いてみよう」
目次
1. はじめに
C# では オートプロパティ({ get; set; } とだけ書くプロパティ)を使うだけで、
‐ 値の保持用フィールド
‐ get / set アクセサ
をまるごとコンパイラに任せられます。
しかし実行時には当然どこかに値を保存する領域が必要です。その“陰の主役”が バッキングフィールド (backing field) です。
本記事では SharpLab で生成 IL を確認したスクリーンショットを題材に、バッキングフィールドの仕組みと実践的な活用ポイントを整理します。
2. オートプロパティとバッキングフィールドとは
用語 | 役割 | コード記述 | 実際に存在する場所 |
---|---|---|---|
オートプロパティ | 値への公開窓口 (public string FirstName { get; set; }) | 開発者が書く | MSIL 変換時に get_FirstName / set_FirstName メソッドへ |
バッキングフィールド | 実データを保持するプライベート変数 | 書かなくても コンパイラが自動生成 | private string <FirstName>k__BackingField; |
ポイント
- 命名規則: <プロパティ名>k__BackingField。k__ は “compiler-generated” の目印
- アクセス修飾子: private(直接触れさせないことでカプセル化を担保)
- 属性付与: [CompilerGenerated] など、デバッグ時に不要表示を抑える属性が自動で付く
3. SharpLab で裏側を確認する
- 左ペインに下記コードを貼り付け
class Person
{
// 名
public string FirstName { get; set; }
}
- 右ペイン「IL」または「C# (decompiled)」を選択
- 生成されたコードを確認

➜ 左側:シンプルなソースコード
➜ 右側:バッキングフィールドを含む生成コード
が並列で比較ますのでイメージしてみましょう。
キャプチャ例では以下のように展開されています(主要部のみ抜粋)。
[CompilerGenerated]
private string <FirstName>k__BackingField;
public string FirstName
{
[CompilerGenerated]
get { return <FirstName>k__BackingField; }
[CompilerGenerated]
set { <FirstName>k__BackingField = value; }
}
4. バッキングフィールドを「自前で」書くケース
目的 | 自動生成 (オートプロパティ) | 明示的フィールド |
---|---|---|
単純な値の格納 | ◎ 手軽 | △ 冗長 |
バリデーション | △ set の中に追加記述が必要 | ◎ フィールドは private、プロパティでチェック |
Lazy-Load / キャッシュ保持 | △ 複雑 | ◎ フィールドを null → 初回アクセスで生成 |
参照渡し (ref/out) が必要 | ✕ プロパティは使えない | ◎ フィールドを直接渡せる |
Tips
- C# 9 以降は init アクセサでイミュータブルパターンも簡潔に書ける
- record 型は “with-式” でコピー/変更を内部で安全に行うため、バッキングフィールドも自動生成されます
5. メリット・デメリットまとめ
オートプロパティ + 自動バッキングフィールド
- ✅ 可読性・記述量の削減
- ✅ DI(依存性注入)フレンドリー
- ❌ 複雑なロジックを追加すると行数が一気に増え、かえって読みにくくなる
明示的バッキングフィールド
- ✅ カスタムロジック(バリデーション、通知、遅延評価)を柔軟に実装
- ✅ ref/out で直接参照を渡せる
- ❌ フィールドとプロパティが分かれ、初心者には冗長に見えやすい
6. サンプルコード:バリデーション付きプロパティ
class Person
{
private string _firstName = "";
/// <summary>
/// 名。空文字や数字のみを禁止するバリデーション例
/// </summary>
public string FirstName
{
get => _firstName;
set
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("FirstName は空にできません");
if (value.Any(char.IsDigit))
throw new ArgumentException("FirstName に数字は含められません");
_firstName = value;
}
}
}
7. まとめ
- バッキングフィールドは「プロパティの裏でデータを保持する実体」
- オートプロパティにより 99% の日常コードは自動生成で OK
- ただし バリデーション/Lazy Load/mutable 構造体の扱い 等では明示的にフィールドを使うと可読性・保守性が向上
- SharpLab など IL ビューアを活用すると「コンパイラがどんなコードを吐いたか」を即確認でき、学習にもデバッグにも役立つ
参考
- SharpLab — C#/IL/ASM 変換をリアルタイムで確認できるオンラインツール
- Microsoft Docs — Auto-Implemented Properties (C# Programming Guide)
- 「Effective C# 7.0」 Item 10 — Prefer Auto-Properties
訪問数 4 回, 今日の訪問数 2回
ディスカッション
コメント一覧
まだ、コメントがありません