プロパティ (C# プログラミング ガイド)
プロパティは、データ フィールドの値の読み取り、書き込み、または計算を行う、柔軟な機構が用意されたメンバーです。 プロパティはパブリック データ メンバーとして表示されますが、アクセサーと呼ばれる特別なメソッドとして実装されます。 この機能によって、データの安全性と柔軟性を高めながら、呼び出し元が簡単にデータにアクセスできます。 プロパティの構文は、フィールドを自然に拡張したものです。 フィールドで格納場所を定義します。
public class Person
{
public string? FirstName;
// Omitted for brevity.
}
自動的に実装されるプロパティ
プロパティの定義には、プロパティの値を取得する get
アクセサーとプロパティに値を割り当てる set
アクセサーの宣言が含まれます。
public class Person
{
public string? FirstName { get; set; }
// Omitted for brevity.
}
前の例は、 自動実装プロパティを示しています。 コンパイラによって、プロパティの非表示バッキング フィールドが生成されます。 また、get
アクセサーと set
アクセサーの本体もコンパイラによって実装されます。 属性はすべて、自動的に実装されるプロパティに適用されます。 属性に field:
タグを指定することで、コンパイラによって生成されたバッキング フィールドに属性を適用できます。
プロパティの右中かっこの後に値を設定することで、プロパティを既定値以外の値に初期化できます。 FirstName
プロパティの初期値は null
より空の文字列の方がよい場合があります。 これは、次のコードに示すように指定します。
public class Person
{
public string FirstName { get; set; } = string.Empty;
// Omitted for brevity.
}
フィールドに基づくプロパティ
C# 13 では、 field
キーワード プレビュー機能を使用して、プロパティのアクセサーに検証またはその他のロジックを追加できます。 field
キーワードは、プロパティのコンパイラ合成バッキング フィールドにアクセスします。 これにより、個別のバッキング フィールドを明示的に宣言せずに、プロパティ アクセサーを記述できます。
public class Person
{
public string? FirstName
{
get;
set => field = value.Trim();
}
// Omitted for brevity.
}
重要
field
キーワードは、C# 13 のプレビュー機能です。 field
コンテキスト キーワードを使用するには、.NET 9 を使用し、<LangVersion>
要素をプロジェクト ファイルにpreview
するように設定する必要があります。
field
という名前のフィールドがあるクラスでは、field
キーワード機能を使用する場合は注意が必要です。 新しい field
キーワードは、プロパティ アクセサーのスコープ内の field
という名前のフィールドをシャドウします。 field
変数の名前を変更するか、@
トークンを使用してfield
識別子を@field
として参照できます。 詳細については、field
キーワード機能の仕様を参照してください。
必須プロパティ
前の例では、呼び出し元が FirstName
プロパティを設定せずに既定のコンストラクタを使用して Person
を作成できます。 プロパティによって型が null 許容文字列に変更されました。 C# 11以降では、呼び出し元にプロパティの設定を強制できます。
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName) => FirstName = firstName;
public required string FirstName { get; init; }
// Omitted for brevity.
}
上記のコードでは、Person
クラスに 2 つの変更を加えています。 1 つ目の FirstName
プロパティの宣言には、required
修飾子が含まれます。 つまり、新しい Person
を作成するコードは、オブジェクト初期化子を使用してこのプロパティを設定する必要があります。 2 つ目の、firstName
パラメーターを受け取るコンストラクターには、System.Diagnostics.CodeAnalysis.SetsRequiredMembersAttribute 属性があります。 この属性は、このコンストラクターが "すべての" required
メンバーを設定することをコンパイラに通知します。 このコンストラクターを使用する呼び出し元は、オブジェクト初期化子で required
プロパティを設定する必要はありません。
重要
required
と "null 非許容" を混同しないでください。 required
プロパティは null
または default
に設定することができます。 これらの例の string
のように、型が null 非許容の場合、コンパイラは警告を発行します。
var aPerson = new Person("John");
aPerson = new Person{ FirstName = "John"};
// Error CS9035: Required member `Person.FirstName` must be set:
//aPerson2 = new Person();
式本体の定義
プロパティ アクセサーは多くの場合、単一行ステートメントで構成されます。 アクセサーは式の結果を割り当てるか返します。 プロパティは、本体が式形式のメンバーとして実装できます。 式本体の定義は、=>
トークンの後に、プロパティに割り当てるかプロパティから取得するための式を続けて構成します。
読み取り専用プロパティに get
アクセサーを式形式のメンバーとして実装できます。 次の例では、読み取り専用 Name
プロパティを式形式のメンバーとして実装しています。
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public required string FirstName { get; init; }
public required string LastName { get; init; }
public string Name => $"{FirstName} {LastName}";
// Omitted for brevity.
}
Name
プロパティは計算対象プロパティです。 Name
のバッキング フィールドはありません。 プロパティは毎回それを計算します。
アクセス制御
前の例では読み取り/書き込みプロパティを示しました。 たとえば、読み取り専用プロパティを作成したり、set アクセサーと get アクセサーに異なるアクセシビリティを設定したりすることもできます。 Person
クラスで、クラス内の他のメソッドからのFirstName
プロパティの値の変更のみを有効にする必要があるとします。 internal
やpublic
の代わりに、set アクセサーprivate
アクセシビリティを付与できます。
public class Person
{
public string? FirstName { get; private set; }
// Omitted for brevity.
}
FirstName
プロパティにはどのコードからもアクセスできる一方で、値の割り当ては Person
クラス内のコードからしかできなくなります。
制限を設定するアクセス修飾子を set アクセサーと get アクセサーのどちらか 1 つに追加することもできます。 個々のアクセサのアクセス修飾子は、プロパティのアクセスよりも制限を厳しくする必要があります。 前のコードは、FirstName
プロパティが public
ですが set アクセサーが private
であるため、有効です。 public
アクセサーを指定して private
プロパティを宣言することはできません。 プロパティの宣言では、protected
、internal
、protected internal
、private
を宣言することもできます。
set
アクセサーには次の 2 つの特殊なアクセス修飾子があります。
set
アクセサーにはアクセス修飾子としてinit
を設定できます。 そのset
アクセサーは、オブジェクト初期化子または型のコンストラクターからのみ呼び出すことができます。set
アクセサーでprivate
よりも制限が厳しくなっています。- 自動的に実装されるプロパティは、
set
アクセサーなしでget
アクセサーを宣言できます。 その場合、コンパイラでは、set
アクセサーを型のコンストラクターからのみ呼び出すことができます。set
アクセサーのinit
アクセサーよりも制限が厳しくなっています。
次のように Person
クラスを変更します。
public class Person
{
public Person(string firstName) => FirstName = firstName;
public string FirstName { get; }
// Omitted for brevity.
}
前の例では、呼び出し元は FirstName
パラメーターを含むコンストラクターを使う必要があります。 呼び出し元は、オブジェクト初期化子を使ってプロパティに値を割り当てることはできません。 初期化子をサポートするには、次のコードで示すように、set
アクセサーを init
アクセサーにすることができます。
public class Person
{
public Person() { }
public Person(string firstName) => FirstName = firstName;
public string? FirstName { get; init; }
// Omitted for brevity.
}
適切な初期化を強制するために、これらの修飾子は required
修飾子と共によく使用されます。
バッキング フィールドを持つプロパティ
計算対象プロパティの概念をプライベート フィールドと組み合わせて、キャッシュ済みの評価されたプロパティを作成できます。 たとえば、最初のアクセス時に文字列の書式設定が行われるように FullName
プロパティを更新します。
public class Person
{
public Person() { }
[SetsRequiredMembers]
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public required string FirstName { get; init; }
public required string LastName { get; init; }
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
この実装は、FirstName
プロパティと LastName
プロパティが読み取り専用であるために機能します。 ユーザーはこれらの名前を変更できます。 set
アクセサーを許可するように FirstName
プロパティと LastName
プロパティを更新するには、fullName
のキャッシュされた値を無効にする必要があります。 fullName
フィールドが再計算されるように、FirstName
プロパティと LastName
プロパティの set
アクセサーを変更します。
public class Person
{
private string? _firstName;
public string? FirstName
{
get => _firstName;
set
{
_firstName = value;
_fullName = null;
}
}
private string? _lastName;
public string? LastName
{
get => _lastName;
set
{
_lastName = value;
_fullName = null;
}
}
private string? _fullName;
public string FullName
{
get
{
if (_fullName is null)
_fullName = $"{FirstName} {LastName}";
return _fullName;
}
}
}
上の最終版では、必要になった場合にのみ FullName
プロパティが評価されます。 以前に計算されたバージョンが有効であれば、それが使用されます。 それ以外の場合、キャッシュされた値が計算によって更新されます。 このクラスを使用するにあたって、開発者は実装の詳細を知っている必要はありません。 内部で変化があっても Person オブジェクトの使用には影響しません。
C# 13 以降では、partial
クラスでpartial
プロパティを作成できます。 partial
プロパティの実装宣言は、自動的に実装されるプロパティにすることはできません。 自動的に実装されるプロパティは、宣言する部分プロパティ宣言と同じ構文を使用します。
プロパティ
プロパティは、クラスまたはオブジェクトに含まれた一種のスマート フィールドです。 オブジェクトの外部からは、オブジェクト内にあるフィールドのように見えます。 一方、プロパティは、C# の機能をどれでも自由に使用して実装できます。 検証、各種アクセシビリティ、遅延評価など、目的のシナリオで必要となる要素はすべて提供できます。
- カスタム アクセサー コードを必要としない単純なプロパティは、式本体の定義として、または 自動的に実装されるプロパティとして実装できます。
- プロパティを使えば、実装や検査コードを隠したままで、値の取得と設定についてパブリックな方法をクラスが公開できます。
- get プロパティ アクセサーはプロパティ値を取得するために使用し、set プロパティ アクセサーは新しい値を割り当てるために使用します。 オブジェクトの構築時にのみ、init プロパティ アクセサーを使用して新しい値が割り当てられます。 これらのアクセサーには異なるアクセス レベルを指定できます。 詳細については、「アクセサーのアクセシビリティの制限」を参照してください。
- キーワード value は、
set
またはinit
アクセサーで割り当てる値を定義するために使用されます。 - プロパティの種類には、読み取り/書き込み (
get
アクセサーとset
アクセサーの両方を備える)、読み取り専用 (get
アクセサーのみでset
アクセサーはない)、書き込み専用 (set
アクセサーのみでget
アクセサーはない) があります。 書き込み専用プロパティはまれです。
C# 言語仕様
詳細については、「C# 言語の仕様」のプロパティに関するセクションを参照してください。 言語仕様は、C# の構文と使用法に関する信頼性のある情報源です。
こちらもご覧ください
.NET