自動実装プロパティの利便性と限界
C#のクラス設計において、データを外部に公開する最も簡単な方法は「自動実装プロパティ」を使用することです。
// 自動実装プロパティ
public int Quantity { get; set; }
この { get; set; } 構文は非常に簡潔ですが、Quantityに値を設定(set)する際に、setの動作を制御することはできません。もし、Quantityに負の数(-50など)や、不正な値が代入されるのを防ぎたい場合、この構文では限界があります。
バッキングフィールドによるロジックの実装
プロパティのset(セッター)やget(ゲッター)に、検証(バリデーション)や計算などのカスタムロジックを実装したい場合は、「バッキングフィールド(Backing Field)」という手法を使用します。
これは、以下の2つの要素で構成されます。
privateなフィールド(バッキングフィールド): 実際のデータを保持するための変数(例:_quantity)。privateのため、クラスの外部からは直接アクセスできません。publicなプロパティ: 外部に公開される「窓口」(例:Quantity)。getやsetのロジックを明示的に記述し、この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)を明示的に定義するパターンに切り替える必要があります。
この手法により、プロパティに不正な値が設定されるのを防ぎ、クラスのデータの整合性を保つ(カプセル化を強化する)ことができます。
