【C#】プロパティとバッキングフィールド:setアクセサーで検証ロジックを実装する方法

目次

自動実装プロパティの利便性と限界

C#のクラス設計において、データを外部に公開する最も簡単な方法は「自動実装プロパティ」を使用することです。

// 自動実装プロパティ
public int Quantity { get; set; }

この { get; set; } 構文は非常に簡潔ですが、Quantityに値を設定(set)する際に、setの動作を制御することはできません。もし、Quantityに負の数(-50など)や、不正な値が代入されるのを防ぎたい場合、この構文では限界があります。


バッキングフィールドによるロジックの実装

プロパティのset(セッター)やget(ゲッター)に、検証(バリデーション)や計算などのカスタムロジックを実装したい場合は、「バッキングフィールド(Backing Field)」という手法を使用します。

これは、以下の2つの要素で構成されます。

  1. privateなフィールド(バッキングフィールド): 実際のデータを保持するための変数(例: _quantity)。privateのため、クラスの外部からは直接アクセスできません。
  2. publicなプロパティ: 外部に公開される「窓口」(例: Quantity)。getsetのロジックを明示的に記述し、このprivateなフィールドにアクセスします。

この構造により、「カプセル化」を維持しつつ、データがどのように読み書きされるかをクラス自身が制御できます。


コード例:プロパティ検証の実装

StockItem(在庫品目)クラスを例に、Quantity(数量)プロパティに「負の数が設定されるのを防ぐ」検証ロジックをバッキングフィールドで実装します。

StockItem クラスの定義

setアクセサー内部では、valueという特別なキーワードが使用できます。これは、プロパティに代入されようとしている「新しい値」を指します。

using System;

/// <summary>
/// 在庫品目を表すクラス
/// </summary>
public class StockItem
{
    // 1. バッキングフィールド (Backing Field)
    // 'Quantity' プロパティの実際の値を格納する private 変数。
    // 慣例としてアンダースコア (_) で始める。
    private int _quantity;

    // 2. 公開されるプロパティ (Property)
    public int Quantity
    {
        // 3. 'get' アクセサー
        // 外部が値を読み取ろうとすると、private な _quantity を返す
        get
        {
            return this._quantity;
        }

        // 4. 'set' アクセサー
        // 外部が値を書き込もうとすると、カスタムロジックが実行される
        set
        {
            // 'value' は、代入されようとしている値 (例: 100 や -50)
            if (value < 0)
            {
                // 不正な値(負の数)が設定されようとした場合
                // 例外をスローして代入を拒否する
                throw new ArgumentOutOfRangeException(
                    nameof(Quantity), // プロパティ名
                    "数量は0以上である必要があります。");
            }
            
            // 検証を通過した場合のみ、バッキングフィールドに値を代入
            this._quantity = value;
        }
    }

    // (参考) Title プロパティは検証が不要なため、自動実装プロパティを使用
    public string Title { get; set; } = "Untitled";
}

クラスの実行例

上記のStockItemクラスを実際に使用し、setアクセサーの検証ロジックが機能することを確認します。

using System;

public class Program
{
    public static void Main()
    {
        var item = new StockItem();

        // --- 1. 成功する代入 ---
        try
        {
            item.Quantity = 100; // 'set' アクセサーが呼び出される
            Console.WriteLine($"正常な代入: {item.Quantity}");
        }
        catch (ArgumentOutOfRangeException ex)
        {
            Console.WriteLine($"エラー: {ex.Message}");
        }

        // --- 2. 失敗する代入(検証に失敗) ---
        try
        {
            // 'set' アクセサーに -50 が渡され、if (value < 0) に該当
            item.Quantity = -50; 
            Console.WriteLine($"不正な代入: {item.Quantity}");
        }
        catch (ArgumentOutOfRangeException ex)
        {
            // if ブロック内の throw が実行され、ここで捕捉される
            Console.WriteLine($"\n'{item.Title}' の在庫設定でエラーが発生しました:");
            Console.WriteLine(ex.Message);
        }
    }
}

出力結果:

正常な代入: 100

'Untitled' の在庫設定でエラーが発生しました:
数量は0以上である必要があります。 (Parameter 'Quantity')

まとめ

自動実装プロパティ({ get; set; })はコードを簡潔に保つのに役立ちますが、set(書き込み)やget(読み取り)の際に検証や追加のロジックが必要になった場合は、バッキングフィールド(private _field)を明示的に定義するパターンに切り替える必要があります。

この手法により、プロパティに不正な値が設定されるのを防ぎ、クラスのデータの整合性を保つ(カプセル化を強化する)ことができます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次