プロパティの使用 (C# プログラミング ガイド)
プロパティは、フィールドとメソッドの両方の側面を結合します。 オブジェクトのユーザーにとってプロパティは、プロパティへのアクセスに同じ構文を必要とするフィールドのように見えます。 クラスの実装者にとってプロパティは、get
アクセサーと set
または init
アクセサーの両方またはいずれかを表す 1 つまたは 2 つのコード ブロックです。 get
アクセサーのコード ブロックはプロパティが読み取られる時に実行され、set
または init
アクセサーのコード ブロックはプロパティに値が割り当てられるときに実行されます。 set
アクセサーのないプロパティは読み取り専用と見なされます。 get
アクセサーのないプロパティは書き込み専用と見なされます。 両方のアクセサーを持つプロパティは、読み取り/書き込みです。 set
アクセサーの代わりに init
アクセサーを使用して、プロパティをオブジェクト初期化の一部として設定できますが、それ以外の場合は読み取り専用になります。
フィールドとは異なり、プロパティは変数には分類されません。 そのため、プロパティを ref
または out
パラメーターとして渡すことはできません。
プロパティには多くの用途があります。
- 変更を許可する前にデータを検証できる。
- データベースなどの他のソースから取得したデータを、クラス上で透過的に公開できる。
- イベントの発生や他のフィールドの値の変更など、データが変更されたときにアクションを実行できる。
プロパティはクラス ブロックで宣言できます。フィールドのアクセス レベル、プロパティの型、プロパティの名前、get
アクセサーと set
アクセサーの両方またはいずれかを宣言するコード ブロックの順で指定します。 次に例を示します。
public class Date
{
private int _month = 7; // Backing store
public int Month
{
get => _month;
set
{
if ((value > 0) && (value < 13))
{
_month = value;
}
}
}
}
この例では、set
アクセサーが Month
が 1 から 12 までの値に設定されていることを確認できるように、Month
がプロパティとして宣言されています。 Month
プロパティは、プライベート フィールドを使用して実際の値を追跡します。 プロパティのデータの実際の場所は、プロパティの "バッキング ストア" と呼ばれることがよくあります。プロパティがプライベート フィールドをバッキング ストアとして使用するのは一般的なことです。 フィールドは、プロパティを呼び出すことでのみ変更できるようにするため、プライベートとマークされます。 パブリックおよびプライベートのアクセス制限の詳細については、「アクセス修飾子」を参照してください。 自動的に実装されるプロパティは、単純なプロパティ宣言の構文を簡略化します。 詳細については、「 自動実装プロパティを参照してください。
C# 13 以降では、次の例に示すように、 フィールドでサポートされるプロパティ を使用して、自動的に実装されるプロパティの set
アクセサーに検証を追加できます。
public class DateExample
{
public int Month
{
get;
set
{
if ((value > 0) && (value < 13))
{
field = value;
}
}
}
}
重要
field
キーワードは、C# 13 のプレビュー機能です。 field
コンテキスト キーワードを使用するには、.NET 9 を使用し、<LangVersion>
要素をプロジェクト ファイルにpreview
するように設定する必要があります。
field
という名前のフィールドがあるクラスでは、field
キーワード機能を使用する場合は注意が必要です。 新しい field
キーワードは、プロパティ アクセサーのスコープ内の field
という名前のフィールドをシャドウします。 field
変数の名前を変更するか、@
トークンを使用してfield
識別子を@field
として参照できます。 詳細については、field
キーワード機能の仕様を参照してください。
get アクセサー
get
アクセサーの本体は、メソッドの本体と似ています。 プロパティの型の値を返す必要があります。 C# コンパイラと Just-In-Time (JIT) コンパイラは、get
アクセサーを実装するための一般的なパターンを検出し、それらのパターンを最適化します。 たとえば、計算を実行せずにフィールドを返す get
アクセサーは、そのフィールドのメモリ読み取りに最適化されている可能性があります。 自動的に実装されるプロパティは、このパターンに従い、これらの最適化の恩恵を受けます。 ただし、仮想 get
アクセサー メソッドはインライン化できません。これは、コンパイラがコンパイル時にどのメソッドが実際に実行時に呼び出されるかを認識しないからです。 次に、プライベート フィールド _name
の値を返す get
アクセサーの例を示します。
class Employee
{
private string _name; // the name field
public string Name => _name; // the Name property
}
プロパティを参照するとき、割り当ての対象を除き、get
アクセサーがプロパティの値を読み取るために呼び出されます。 次に例を示します。
var employee= new Employee();
//...
System.Console.Write(employee.Name); // the get accessor is invoked here
get
アクセサーは式形式のメンバーであるか、return または throw ステートメントで終わる必要があり、コントロールはアクセサー本体からフロー オフすることはできません。
警告
一般に、 get
アクセサーを使用してオブジェクトの状態を変更するのは不適切なプログラミング スタイルです。 このルールの 1 つの例外は、プロパティの値が最初にアクセスされたときにのみ計算される 評価 プロパティです。
get
アクセサーは、フィールド値を返すまたは計算してから返すために使用できます。 次に例を示します。
class Manager
{
private string _name;
public string Name => _name != null ? _name : "NA";
}
前の例では、Name
プロパティに値を割り当てないと、値 NA
が返されます。
set アクセサー
set
アクセサーは、戻り値の型が void のメソッドと似ています。 型がプロパティの型の value
と呼ばれる暗黙のパラメーターを使用します。 コンパイラと JIT コンパイラは、set
または init
アクセサーの一般的なパターンも認識します。 これらの一般的なパターンが最適化され、バッキング フィールドのメモリが直接書き込まれます。 次の例では、set
アクセサーが Name
プロパティに追加されます。
class Student
{
private string _name; // the name field
public string Name // the Name property
{
get => _name;
set => _name = value;
}
}
プロパティに値を割り当てるときに、新しい値を提供する引数を使用して set
アクセサーが呼び出されます。 次に例を示します。
var student = new Student();
student.Name = "Joe"; // the set accessor is invoked here
System.Console.Write(student.Name); // the get accessor is invoked here
set
アクセサーでローカル変数の宣言に暗黙のパラメーター名 value
を使用すると、エラーになります。
init アクセサー
init
アクセサーを作成するコードは、set
の代わりに init
キーワードを使用するという点を除き、set
アクセサーを作成するコードと同じです。 init
アクセサーはコンストラクター内で使用するか、オブジェクト初期化子で使用するしかないというところが違います。
解説
プロパティは public
、private
、protected
、internal
、protected internal
、private protected
のいずれかでマークできます。 これらのアクセス修飾子により、クラスのユーザーがプロパティにアクセスできる方法が定義されます。 同じプロパティの get
と set
アクセサーは、異なるアクセス修飾子を持つことができます。 たとえば、get
を public
にして、型の外部からの読み取り専用アクセスを許可して、set
を private
または protected
にすることができます。 詳細については、「アクセス修飾子」を参照してください。
static
キーワードを使用して、プロパティを静的プロパティとして宣言できます。 静的プロパティは、クラスのインスタンスが存在しなくても、呼び出し元でいつでもを使用できます。 詳細については、「静的クラスと静的クラス メンバー」を参照してください。
プロパティは、virtual キーワードを使用して仮想プロパティとしてマークできます。 仮想プロパティを使うと、派生クラスで override キーワードを使ってプロパティの動作をオーバーライドできます。 これらのオプションの詳細については、「継承」を参照してください。
仮想プロパティをオーバーライドするプロパティは、sealed にすることもできます。その場合、派生クラスでは、プロパティが仮想でなくなります。 最後に、プロパティは抽象として宣言できます。 抽象プロパティではクラスの実装は定義されておらず、派生クラスで独自の実装を記述する必要があります。 これらのオプションの詳細については、「抽象クラスとシール クラス、およびクラス メンバー」を参照してください。
例
この例では、インスタンス、静的、および読み取り専用のプロパティを示します。 キーボードから従業員の名前を受け取り、NumberOfEmployees
を 1 だけインクリメントし、従業員の名前と番号を表示します。
public class Employee
{
public static int NumberOfEmployees;
private static int _counter;
private string _name;
// A read-write instance property:
public string Name
{
get => _name;
set => _name = value;
}
// A read-only static property:
public static int Counter => _counter;
// A Constructor:
public Employee() => _counter = ++NumberOfEmployees; // Calculate the employee's number:
}
プロパティ非表示の例
この例では、派生クラスで同じ名前を持つ別のプロパティによって非表示にされている基底クラスのプロパティにアクセスする方法を示します。
public class Employee
{
private string _name;
public string Name
{
get => _name;
set => _name = value;
}
}
public class Manager : Employee
{
private string _name;
// Notice the use of the new modifier:
public new string Name
{
get => _name;
set => _name = value + ", Manager";
}
}
class TestHiding
{
public static void Test()
{
Manager m1 = new Manager();
// Derived class property.
m1.Name = "John";
// Base class property.
((Employee)m1).Name = "Mary";
System.Console.WriteLine("Name in the derived class is: {0}", m1.Name);
System.Console.WriteLine("Name in the base class is: {0}", ((Employee)m1).Name);
}
}
/* Output:
Name in the derived class is: John, Manager
Name in the base class is: Mary
*/
前の例で重要な点を次に示します。
- 派生クラスのプロパティ
Name
により基底クラス内のプロパティName
が非表示になっています。 このような場合、new
修飾子は派生クラスのプロパティの宣言で使用されます。public new string Name
- キャスト
(Employee)
は基底クラスで非表示のプロパティにアクセスするために使用されます。((Employee)m1).Name = "Mary";
メンバーを非表示にする詳細については、「new 修飾子」を参照してください。
プロパティ オーバーライドの例
この例では、Cube
と Square
の 2 つのクラスが抽象クラス Shape
を実装し、その抽象 Area
プロパティをオーバーライドします。 プロパティでの override 修飾子の使用に注意してください。 プログラムは、入力として辺を受け入れ、四角形と立方体の面積を計算します。 プログラムはまた、入力として面積を受け入れ、四角形と立方体の対応する辺を計算します。
abstract class Shape
{
public abstract double Area
{
get;
set;
}
}
class Square : Shape
{
public double side;
//constructor
public Square(double s) => side = s;
public override double Area
{
get => side * side;
set => side = System.Math.Sqrt(value);
}
}
class Cube : Shape
{
public double side;
//constructor
public Cube(double s) => side = s;
public override double Area
{
get => 6 * side * side;
set => side = System.Math.Sqrt(value / 6);
}
}
class TestShapes
{
static void Main()
{
// Input the side:
System.Console.Write("Enter the side: ");
double side = double.Parse(System.Console.ReadLine());
// Compute the areas:
Square s = new Square(side);
Cube c = new Cube(side);
// Display the results:
System.Console.WriteLine("Area of the square = {0:F2}", s.Area);
System.Console.WriteLine("Area of the cube = {0:F2}", c.Area);
System.Console.WriteLine();
// Input the area:
System.Console.Write("Enter the area: ");
double area = double.Parse(System.Console.ReadLine());
// Compute the sides:
s.Area = area;
c.Area = area;
// Display the results:
System.Console.WriteLine("Side of the square = {0:F2}", s.side);
System.Console.WriteLine("Side of the cube = {0:F2}", c.side);
}
}
/* Example Output:
Enter the side: 4
Area of the square = 16.00
Area of the cube = 96.00
Enter the area: 24
Side of the square = 4.90
Side of the cube = 2.00
*/
関連項目
.NET