Galileo Figaro

常に初陣!

C# プロパティ 基礎

プロパティが誕生した背景

オブジェクト指向の中で重要な考え方として「カプセル化」というものがあります。 カプセル化とは、データとそれを操作するメソッドを一つのモジュールとしてまとめることであり、 データはそのメソッドを通して操作するようにしなければいけません。

そのため、オブジェクト指向プログラミングでは、 フィールドとその値を取得・設定するための Getter および Setter を作成し、 フィールドは private にして外部から見えないようにすべきです。 これを単純に実装すると、例えば次のようなコードになるでしょう。

class Person
{
    private int age; // データを保持する変数 (フィールド)

    public void SetAge(int age)
    {
        this.age = age;
    }

    public int GetAge()
    {
        return age;
    }
}

基本にとても忠実です。素晴らしい。 でも、下記のようにちょっとイケてないです。

見た目がイケてない

1つのフィールドに対して、Get/Setという2つのメソッドを書くことになるし、 冗長で打鍵量も多いです。 前述の例だと、「Age」という文字列を 2 回も打たなければならないし、 メソッドなので引数リストも書かなくちゃいけないです。

外部から使うときもイケてない

クラス外からフィールドを変更するには、 これらの Getter/Setter を通さないといけないです。 例えば次のように取得・設定することになります。

person.SetAge(42);
int age = person.GetAge();

クラスの利用者にしてみれば、 インスタンス内のデータを取得したり設定したりしたいだけなので、 ぶっちゃけフィールドに直接アクセスできた方が楽です。

// age への直接アクセスはダメだけど、こう書けた方がスッキリするよね
person.age = 42;
int age = person.age;

プロパティとは

「プロパティ」は、前述のような背景があり生まれた C# の構文です。

アクセス修飾子 型 名前
{
    get
    {
        // Getter の処理
    }
    set
    {
        // Setter の処理
    }
}

例えば、前述の「年齢の取得・設定」の例は次のようになります。

class Person
{
    private int age;

    public int Age
    {
        get
        {
            return age;
        }
        set
        {
            age = value;
        }
    }
}

利用側は次のようになります。 Age という変数に直接アクセスしているように見えますが、 実際には Getter や Setter が呼ばれます。

person.Age = 42;
int age = person.Age;

このように、「クラスの実装者にとってはメソッドだけど、 クラスの利用者にとってはデータに直接アクセスしているように見える」 というのがプロパティです。

プロパティの導入によって、

  • Getter/Setter がひとまとまりになり、冗長な文字列も書かなくてよくなります。
  • 利用側からは直接フィールドの値を取得・設定しているように見え、スッキリします。

C#では、インスタンス内のデータを取得したり設定したりするときに、 通常のメソッドの形で Getter/Setter を用意することはまず無いです。 プロパティを作るようにしましょう。

なお、この例の age のようなフィールドのことを、 バッキングフィールド (backing field) と言います。 クラス外に見せるプロパティに対して、 「そのプロパティの背後にあるフィールド」 という意味合いからそう呼ばれているのだと思います。

自動実装プロパティ

前述のように、単に値の取得だけをする Getter や、 値の設定だけをする Setter を、プロパティとして用意したとしても、 結構な行数を必要としてしまいます。

private int age;
public int Age
{
    get
    {
        return age;
    }
    set
    {
        age = value;
    }
}

C# のインデント方式では、ブロックの始まりのカッコ「{」だけでも改行をするのが一般的ですので、 単に取得・設定するだけでも、12行も使うことになってしまいます。

単に取得・設定するだけのコードであれば、 コンパイラに自動で生成させちゃいましょう、 ってことで上記のコードは下記のように省略することができます。 これは自動実装プロパティと呼ばれます。

public int Age { get; set; }

この 1 行のコードと、前述の 12 行のコードは、同じ意味です。 自動実装プロパティを使うと、コンパイラによって、 自動でバッキングフィールドが作られ、その値を取得・設定するようなコードも自動生成されます。