プロパティは、データ フィールドの値の読み取り、書き込み、または計算を行う、柔軟な機構が用意されたメンバーです。 プロパティはパブリック データ メンバーとして表示されますが、アクセサーと呼ばれる特別なメソッドとして実装されます。 この機能によって、データの安全性と柔軟性を高めながら、呼び出し元が簡単にデータにアクセスできます。 プロパティの構文は、フィールドを自然に拡張したものです。 フィールドで格納場所を定義します。
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# 14 では、 field キーワードを使用して、プロパティのアクセサーに検証またはその他のロジックを追加できます。
field キーワードは、プロパティのコンパイラ合成バッキング フィールドにアクセスします。 これにより、個別のバッキング フィールドを明示的に宣言せずに、プロパティ アクセサーを記述できます。
public class Person
{
public string? FirstName
{
get;
set => field = value.Trim();
}
// Omitted for brevity.
}
必須プロパティ
前の例では、呼び出し元が Person プロパティを設定せずに既定のコンストラクタを使用して FirstName を作成できます。 プロパティによって型が null 許容文字列に変更されました。 呼び出し元にプロパティの設定を 要求 できます。
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 プロパティの値の変更のみを有効にする必要があるとします。
privateやinternalの代わりに、set アクセサーpublicアクセシビリティを付与できます。
public class Person
{
public string? FirstName { get; private set; }
// Omitted for brevity.
}
FirstName プロパティにはどのコードからもアクセスできる一方で、値の割り当ては Person クラス内のコードからしかできなくなります。
制限を設定するアクセス修飾子を set アクセサーと get アクセサーのどちらか 1 つに追加することもできます。 個々のアクセサのアクセス修飾子は、プロパティのアクセスよりも制限を厳しくする必要があります。 前のコードは、FirstName プロパティが public ですが set アクセサーが private であるため、有効です。
private アクセサーを指定して public プロパティを宣言することはできません。 プロパティの宣言では、protected、internal、protected internal、private を宣言することもできます。
set アクセサーには次の 2 つの特殊なアクセス修飾子があります。
-
setアクセサーにはアクセス修飾子としてinitを設定できます。 そのsetアクセサーは、オブジェクト初期化子または型のコンストラクターからのみ呼び出すことができます。privateアクセサーでsetよりも制限が厳しくなっています。 - 自動的に実装されるプロパティは、
getアクセサーなしでsetアクセサーを宣言できます。 その場合、コンパイラでは、setアクセサーを型のコンストラクターからのみ呼び出すことができます。initアクセサーのsetアクセサーよりも制限が厳しくなっています。
次のように 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 プロパティが読み取り専用であるために機能します。 ユーザーはこれらの名前を変更できます。
FirstName アクセサーを許可するように LastName プロパティと set プロパティを更新するには、fullName のキャッシュされた値を無効にする必要があります。
set フィールドが再計算されるように、FirstName プロパティと LastName プロパティの fullName アクセサーを変更します。
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 プロパティの実装宣言は、自動的に実装されるプロパティにすることはできません。 自動的に実装されるプロパティは、宣言する部分プロパティ宣言と同じ構文を使用します。
プロパティ
プロパティは、クラスまたはオブジェクトに含まれた一種のスマート フィールドです。 オブジェクトの外部からは、オブジェクト内にあるフィールドのように見えます。 一方、プロパティは、C# の機能をどれでも自由に使用して実装できます。 検証、各種アクセシビリティ、遅延評価など、目的のシナリオで必要となる要素はすべて提供できます。
- カスタム アクセサー コードを必要としない単純なプロパティは、式本体の定義として、または 自動的に実装されるプロパティとして実装できます。
- プロパティを使えば、実装や検査コードを隠したままで、値の取得と設定についてパブリックな方法をクラスが公開できます。
- get プロパティ アクセサーはプロパティ値を取得するために使用し、set プロパティ アクセサーは新しい値を割り当てるために使用します。 オブジェクトの構築時にのみ、init プロパティ アクセサーを使用して新しい値が割り当てられます。 これらのアクセサーには異なるアクセス レベルを指定できます。 詳細については、「アクセサーのアクセシビリティの制限」を参照してください。
- キーワード value は、
setまたはinitアクセサーで割り当てる値を定義するために使用されます。 - プロパティの種類には、読み取り/書き込み (
getアクセサーとsetアクセサーの両方を備える)、読み取り専用 (getアクセサーのみでsetアクセサーはない)、書き込み専用 (setアクセサーのみでgetアクセサーはない) があります。 書き込み専用プロパティはまれです。
C# 言語仕様
詳細については、「C# 言語の仕様」のプロパティに関するセクションを参照してください。 言語仕様は、C# の構文と使用法に関する信頼性のある情報源です。
こちらもご覧ください
.NET