カプセル化のためのプロパティ
CoddyのC#ジャーニー「オブジェクト指向プログラミング」セクションの一部 — レッスン 32/70。
プロパティは、C# においてカプセル化を実装するための主要なツールです。フィールドを直接公開する代わりに、データのアクセスや変更方法を制御するプロパティでそれらをラップします。
このパターンは、プライベートフィールドと、アクセサー内にバリデーションやロジックを含むパブリックプロパティを組み合わせたものです:
public class Temperature
{
private double celsius;
public double Celsius
{
get { return celsius; }
set
{
if (value < -273.15)
celsius = -273.15; // 絶対零度の最小値
else
celsius = value;
}
}
}これで、外部のコードが不可能な温度を設定することはできなくなりました。プロパティがゲートキーパー(門番)として機能し、オブジェクトが常に有効な状態に保たれることを保証します。これがカプセル化の実践です。内部データは保護されつつ、制御されたインターフェースを通じてアクセス可能な状態が維持されます。
プロパティを使用して値を計算したり、副作用(サイドエフェクト)を発生させたりすることもできます。
public double Fahrenheit
{
get { return celsius * 9 / 5 + 32; }
set { celsius = (value - 32) * 5 / 9; }
}外部からは2つのプロパティが見えますが、内部的にはフィールドは1つだけです。プロパティを使用すると、クラスを利用するコードを壊すことなく実装の詳細を変更できます。これは、適切なカプセル化の主な利点です。
チャレンジ
簡単プロパティを使用してデータの整合性を保護する、製品在庫システムを構築しましょう。価格が負の値にならず、数量が常に有効な範囲内に保たれるクラスを作成します。これにより、プロパティがデータのゲートキーパー(門番)としてどのように機能するかを学びます。
コードは2つのファイルに分けて構成します:
Product.cs:Inventory名前空間内に、プロパティによるバリデーション(検証)を行って製品情報を管理するProductクラスを作成します。製品には以下のものが必要です:- プライベートフィールド
price(decimal) と、価格が0未満にならないことを保証するパブリックなPriceプロパティ。負の価格を設定しようとした場合は、代わりに0として保存されるようにします。 - プライベートフィールド
quantity(int) と、値を0から1000の間に保つパブリックなQuantityプロパティ。0未満の値は0になり、1000を超える値は1000になります。 - パブリックな自動実装プロパティ
Name(string) Price * Quantityを返す、計算済みの読み取り専用プロパティTotalValue
- プライベートフィールド
Program.cs: メインファイルで、入力値を使用してProductを作成します。次に、無効な可能性がある追加の入力値を使用して、価格と数量の変更を試みます。作成後および各変更後の製品の状態を出力して、プロパティがどのようにデータを保護しているかを確認してください。
5つの入力を受け取ります:
- 製品名
- 初期価格 (decimal)
- 初期数量 (integer)
- 新しく設定する価格 (decimal、負の値の可能性あり)
- 新しく設定する数量 (integer、範囲外の可能性あり)
出力は以下の形式で表示してください:
After creation:
{Name}: Price={Price}, Qty={Quantity}, Total={TotalValue}
After modifications:
{Name}: Price={Price}, Qty={Quantity}, Total={TotalValue}例えば、入力が Laptop, 999.99, 50, -100, 1500 の場合、出力は以下のようになります:
After creation:
Laptop: Price=999.99, Qty=50, Total=49999.50
After modifications:
Laptop: Price=0, Qty=1000, Total=0負の価格が 0 になり、1500 という数量が 1000 に制限されていることに注目してください。プロパティがオブジェクトを無効な状態から保護しています。これがカプセル化の仕組みです!
チートシート
プロパティは、get および set アクセサーを介した制御されたアクセスによってプライベートフィールドをラップし、バリデーションとカプセル化を可能にします。
public class Temperature
{
private double celsius;
public double Celsius
{
get { return celsius; }
set
{
if (value < -273.15)
celsius = -273.15;
else
celsius = value;
}
}
}プロパティは、内部フィールドに基づいて値を計算できます。
public double Fahrenheit
{
get { return celsius * 9 / 5 + 32; }
set { celsius = (value - 32) * 5 / 9; }
}読み取り専用プロパティは set アクセサーを省略し、計算された値または保存された値の取得のみを許可します。
自分で試してみよう
using System;
using Inventory;
class Program
{
public static void Main(string[] args)
{
// 入力を読み込む
string name = Console.ReadLine();
decimal initialPrice = Convert.ToDecimal(Console.ReadLine());
int initialQuantity = Convert.ToInt32(Console.ReadLine());
decimal newPrice = Convert.ToDecimal(Console.ReadLine());
int newQuantity = Convert.ToInt32(Console.ReadLine());
// TODO: 初期値を使用してProductを作成する
// TODO: 作成後の製品の状態を出力する
Console.WriteLine("After creation:");
// 出力形式: {Name}: Price={Price}, Qty={Quantity}, Total={TotalValue}
// TODO: 新しい値で価格と数量を変更する
// TODO: 変更後の製品の状態を出力する
Console.WriteLine("After modifications:");
// 出力形式: {Name}: Price={Price}, Qty={Quantity}, Total={TotalValue}
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。