次の方法で共有


プロパティ (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.
}

アクセス制御

前の例では読み取り/書き込みプロパティを示しました。 たとえば、読み取り専用プロパティを作成したり、set アクセサーと get アクセサーに異なるアクセシビリティを設定したりすることもできます。 具体例として、Person クラスで、クラス内の他のメソッドからのみ FirstName プロパティの値を変更できるようにしたい場合は、 set アクセサーのアクセシビリティを public ではなく private に設定します。

public class Person
{
    public string? FirstName { get; private set; }

    // Omitted for brevity.
}

FirstName プロパティにはどのコードからもアクセスできる一方で、値の割り当ては Person クラス内のコードからしかできなくなります。

制限を設定するアクセス修飾子を set アクセサーと get アクセサーのどちらか 1 つに追加することもできます。 個々のアクセサのアクセス修飾子は、プロパティのアクセスよりも制限を厳しくする必要があります。 前のコードは、FirstName プロパティが public ですが set アクセサーが private であるため、有効です。 public アクセサーを指定して private プロパティを宣言することはできません。 プロパティの宣言では、protectedinternalprotected internalprivate を宣言することもできます。

set アクセサーには次の 2 つの特殊なアクセス修飾子があります。

  • set アクセサーにはアクセス修飾子として init を設定できます。 その set アクセサーは、オブジェクト初期化子または型のコンストラクターからのみ呼び出すことができます。 set アクセサーで private よりも制限が厳しくなっています。
  • 自動的に実装されるプロパティは、アクセサーなしでアクセサーをgetset宣言できます。 その場合、コンパイラでは、 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 修飾子と共によく使用されます。

必須プロパティ

前の例では、呼び出し元が 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 のバッキング フィールドはありません。 プロパティは毎回それを計算します。

バッキング フィールドを持つプロパティ

計算対象プロパティの概念をプライベート フィールドと組み合わせて、キャッシュ済みの評価されたプロパティを作成できます。 たとえば、最初のアクセス時に文字列の書式設定が行われるように 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# の構文と使用法に関する信頼性のある情報源です。

こちらもご覧ください