バッキングフィールド
CoddyのC#ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 17/70。
自動実装プロパティは便利ですが、プロパティが値を格納または取得する方法をより詳細に制御する必要がある場合があります。そこで登場するのがバッキングフィールドです。バッキングフィールドは、プロパティの実際のデータを格納するプライベートフィールドです。
public class Product
{
private decimal _price; // バッキングフィールド
public decimal Price
{
get { return _price; }
set { _price = value; }
}
}これは自動プロパティと比較すると手間がかかるように見えますが、バッキングフィールドを使用するとロジックを追加できます。例えば、データを保存する前に検証を行うことができます。
public class Product
{
private decimal _price;
public decimal Price
{
get => _price;
set => _price = value < 0 ? 0 : value;
}
}これで、負の価格は自動的にゼロになります。また、読み取りや書き込み時にデータを変換したり、変更をログに記録したり、他のアクションをトリガーしたりすることもできます。
バッキングフィールドの命名規則では、通常、プロパティと区別するために、アンダースコアの接頭辞(_price)または小文字(price)を使用します。
public decimal Price { get; set; } のような自動実装プロパティを使用すると、コンパイラは実際には隠れたバッキングフィールドを自動的に作成します。明示的なバッキングフィールドを使用すると、動作を完全に制御しながら、同じストレージを確保できます。
チャレンジ
簡単バッキングフィールドを使用して、データの保存時や取得時にデータの検証と変換を行う温度センサーシステムを構築しましょう。
コードを整理するために、2つのファイルを作成します。
TemperatureSensor.cs:Sensors名前空間内にTemperatureSensorクラスを定義します。このセンサーは摂氏で温度を測定しますが、測定値が有効な範囲内に収まるようにする必要があります。バッキングフィールドを使用して、以下を実装してください。- バッキングフィールド
_locationを持つLocationプロパティ(string)。場所を設定する際、値が null または空の場合は、代わりに"Unknown"を保存します。 - バッキングフィールド
_temperatureを持つTemperatureプロパティ(double)。温度を設定する際、値を-50から150の間にクランプします(-50 未満の場合は -50 を保存し、150 を超える場合は 150 を保存します)。 - 華氏での温度を計算して返す読み取り専用プロパティ
TemperatureFahrenheit。計算式(celsius * 9 / 5) + 32を使用します。これはバッキングフィールドを直接使用する必要があります。 - 場所と初期温度の値を受け取るコンストラクタ。
- バッキングフィールド
Program.cs: メインファイルで、入力値を使用してセンサーを作成し、その測定値を表示します。次に、2番目の入力値で温度を更新し、更新された測定値を表示します。
3つの入力を受け取ります:
- センサーの場所
- 初期温度の測定値
- 更新された温度の測定値
出力は以下の形式で表示してください:
Sensor: {Location}
Temperature: {Temperature}C ({TemperatureFahrenheit}F)
Updated Temperature: {Temperature}C ({TemperatureFahrenheit}F)例えば、入力が Lab Room、25、200 の場合、出力は以下のようになります:
Sensor: Lab Room
Temperature: 25C (77F)
Updated Temperature: 150C (302F)2番目の測定値 200 が、有効な最大温度を超えているため 150 にクランプされていることに注目してください。バッキングフィールドを使用することで、データの保存方法をこのように制御できます!
チートシート
バッキングフィールド(裏付けフィールド)は、プロパティの実際のデータを格納するプライベートフィールドであり、値の取得や設定にカスタムロジックを追加することを可能にします。
基本的なバッキングフィールドの構文
public class Product
{
private decimal _price; // バッキングフィールド
public decimal Price
{
get { return _price; }
set { _price = value; }
}
}式形式(Expression-Bodied)の構文
public decimal Price
{
get => _price;
set => _price = value;
}バリデーションロジックの追加
バッキングフィールドを使用すると、データを保存する前に検証(バリデーション)や変換を行うことができます。
private decimal _price;
public decimal Price
{
get => _price;
set => _price = value < 0 ? 0 : value; // 負の価格を防ぐ
}命名規則
バッキングフィールドは通常、プロパティ名と区別するために、アンダースコアのプレフィックス(_price)または小文字(price)を使用します。
自動実装プロパティ vs バッキングフィールド
public decimal Price { get; set; } のような自動実装プロパティは、隠れたバッキングフィールドを自動的に作成します。明示的なバッキングフィールドを使用すると、同じストレージを維持しながら、動作を完全に制御できます。
自分で試してみよう
using System;
using Sensors;
class Program
{
public static void Main(string[] args)
{
// 入力を読み込む
string location = Console.ReadLine();
double initialTemp = Convert.ToDouble(Console.ReadLine());
double updatedTemp = Convert.ToDouble(Console.ReadLine());
// TODO: location と initial temperature を使用して TemperatureSensor を作成する
// TODO: センサーの場所と初期温度の読み取り値を出力する
// 形式: "Sensor: {Location}"
// 形式: "Temperature: {Temperature}C ({TemperatureFahrenheit}F)"
// TODO: センサーの温度を updated value で更新する
// TODO: 更新された温度の読み取り値を出力する
// 形式: "Updated Temperature: {Temperature}C ({TemperatureFahrenheit}F)"
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。