CodeGym /コース /C# SELF /C#のプロパティ (Properties)

C#のプロパティ (Properties)

C# SELF
レベル 17, レッスン 1
使用可能

1. はじめに

まずはフィールドを直接使うときに起きる問題から始めよう。もしフィールドをpublicで宣言したら、プログラムのどこからでも即座に値を変えられちゃうんだ:

public class Dog
{
    public string Name;
}

Dog dog = new Dog();
dog.Name = ""; // O_o ... これって「犬の名前 = 空文字列」ってことだよ!

ユーザーはNameフィールドに、意味不明な値も平気で入れられる。例えば空文字列、長すぎる名前、あるいはnullとかね。これはまるで、君の新しいIKEAの棚を誰かが組み立ててるのに、木工工場の全アクセス権を渡しちゃうようなもんだ。

覚えておいて、カプセル化っていうのは、オブジェクトが自分のデータを自分でコントロールすること。他人に内部を全部見せるんじゃなくて、特別な「ドア」— プロパティ (Properties) を用意して、そこを通してアクセスさせる。しかもチェックやログ、値の変更、他の反応もできるんだ。

2. 定義とシンタックス

プロパティは、見た目はフィールドっぽいけど、実は裏で特別なメソッドのペア:getter(値を取得)とsetter(値をセット)が動いてる。プロパティを使うと:

  • データの読み書きを許可したり禁止したりできる;
  • アクセス時にバリデーションやロジックを追加できる;
  • 内部フィールドを隠したり、値を別の場所に保存したりもできる。

プロパティはフィールドに似てるけど、波カッコとgetとsetキーワードが中に入るのが特徴。


[アクセス修飾子] 型 プロパティ名
{
    get { ... }
    set { ... }
}
プロパティ宣言のテンプレート

クラスDogの例を見てみよう:

public class Dog
{
    private string _name; // 内部フィールド (private!)

    public string Name
    {
        get { return _name; }  // 「ゲッター」:名前を取得
        set { _name = value; } // 「セッター」:名前をセット
    }
}

メモ:アンダースコアは普通、プライベートフィールド(_name)に使う。これはC#のスタイル標準だよ。

3. プロパティの仕組み

プロパティは、オブジェクトの内部データと外の世界の間に立つ「ガードマン」みたいなもの。例を見てみよう:

Dog dog = new Dog();
dog.Name = "シャリク";
Console.WriteLine(dog.Name);

解説:

  • プログラムがdog.Name = "シャリク";の行に来たとき、プロパティのsetメソッドが呼ばれる。ここで好きなチェック(例えば名前が空じゃないか)を追加できる。
  • Console.WriteLine(dog.Name);のときはgetメソッドが呼ばれて、今の値を返すか、必要なら動的に計算した値を返す。

見た目は普通のフィールドみたいだけど、実は全部コントロールできるんだ!

4. なぜプロパティが「Best Practice」なのか

ほとんどの場合、オブジェクトの内部フィールドに直接アクセスはさせない。今はチェックが不要に思えても、データをプロパティで包むクセをつけておくと、ルールが変わったときにめっちゃ助かるよ。

「バリデーション」付きの例:


public class Dog
{
    private string _name;

    public string Name
    {
        get { return _name; }
        set
        {
            if (string.IsNullOrWhiteSpace(value))
            {
                throw new ArgumentException("犬の名前は空にできないよ!");
            }
            _name = value;
        }
    }
}

これでこんなことをすると:

dog.Name = ""; // 例外が投げられる!

…変な値からオブジェクトを守れるんだ。

5. プロパティ:読み取り専用、書き込み専用、普通のプロパティ

時々、値の参照だけ許したいときがある(例えば犬の生年は変えられない)、逆にセットだけ許したい(レアだけど、何か謎なことをしたいとき)。

  • 読み取り専用:getだけ書いて、setは消す。
  • 書き込み専用:setだけ書いて、getは消す。

例:

public class Dog
{
    private int _birthYear = 2018;

    // 読み取り専用
    public int BirthYear
    {
        get { return _birthYear; }
    }

    // 書き込み専用(かなりレア)
    public string Secret
    {
        set { /* valueで何かする */ }
    }
}

6. プロパティ vs フィールド

フィールド プロパティ
シンタックス
public string Name;
public string Name { get; set; }
アクセス 直接 get/set経由
バリデーション なし set/getに追加できる
拡張性 なし いつでも変更できる
IDE連携 フィールドとして見える プロパティとして見える(フレームワークで重要)

イラスト:プロパティの動作

sequenceDiagram
participant User as オブジェクトのユーザー
participant Dog as Dogオブジェクト
participant Field as プライベートフィールド _name

User->>Dog: dog.Name = "リジク"
Dog->>Dog: set Name("リジク")
Dog->>Field: _name = "リジク"

User->>Dog: print(dog.Name)
Dog->>Dog: get Name()
Dog->>Field: _nameを読む
Dog->>User: "リジク"を返す

7. よくあるミスと注意点

どこに落とし穴があるか見てみよう。

よくあるミスは、フィールドとプロパティを混同して、プライベートデータをうっかり外に公開しちゃうこと:

public string name; // これはフィールド!どこからでも見えちゃう、危険!

こうした方がいい:

private string _name;
public string Name
{
    get { return _name; }
    set { _name = value; }
}

staticプロパティもあって、全オブジェクト共通の値を保存できる。でも使いすぎ注意だよ。

面白い話:プロパティにgetだけあってコンストラクタがなければ、値は絶対に変えられない—これがimmutable propertyってやつで、最近の設計でよく使われる(recordやデータの不変性の講義で詳しくやるよ)。

2
タスク
C# SELF, レベル 17, レッスン 1
ロック未解除
get/set アクセスを持つプロパティの宣言
get/set アクセスを持つプロパティの宣言
2
タスク
C# SELF, レベル 17, レッスン 1
ロック未解除
入力値の検証付きプロパティ
入力値の検証付きプロパティ
コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION