フィールドとプロパティ ― はじめて学ぶ人のための基礎まとめ

1. 用語の整理

用語ひと言でどこで定義する?何のため?
フィールド (field)“値をしまう箱”クラスの中でメンバ変数として宣言内部データを保持する
プロパティ (property)“箱に出入口を付けたもの”クラスの中で Type 名前 { get; set; } の形で宣言フィールドへの安全なアクセス窓口

イメージ

  • フィールド = 倉庫の中身そのもの
  • プロパティ = 倉庫の搬入口(受付で検品や記録ができる)

2. なぜプロパティを使うのか

  1. カプセル化
    • クラス外から 直接 値を触らせず、「操作方法」を制御できます。
  2. 不正値のブロック
    • set 内にチェックを入れて「0〜100 以外は受け付けない」などのバリデーションが可能。
  3. 読み取り専用・書き込み専用が選べる
    • 例:public int Id { get; } なら外部は読み取りだけ。
  4. あとから内部構造を変えても呼び出し側は影響なし
    • 最初はフィールド直結 → 後で計算式に変更してもプロパティ名は同じなので既存コードが壊れません。
  5. デバッグ・ロギング・通知が入れられる
    • 例:WPF や Unity の INotifyPropertyChanged でデータバインディング。

3. 書き方のバリエーション

3-1. フィールド

public class Player
{
    // 外部から直接読める・書ける(丸裸)
    public int Score;
}

3-2. 自動実装プロパティ(最もシンプル)

public class Player
{
    // 裏でフィールドが自動生成される
    public int Score { get; set; }
}

3-3. バリデーション付きプロパティ

public class Player
{
    private int _score;          // 実体(private フィールド)

    public int Score             // 公開窓口
    {
        get => _score;
        set
        {
            if (value < 0) value = 0;   // マイナスは許さない
            _score = value;
        }
    }
}
using System;

public class Player
{
    // ───────── フィールド(実体)─────────
    // 外部に直接さらさず、クラスの内部データを保持する場所。
    private int _score;

    // ───────── プロパティ(公開窓口)─────────
    // ・get  … 現在のスコアをそのまま返す。
    // ・set  … 受け取った値を検査し、マイナスなら 0 に補正して保存する。
    public int Score
    {
        get => _score;

        set
        {
            // ① バリデーション:あり得ない値は修正または拒否
            if (value < 0) value = 0;

            // ② フィールドへ保存
            _score = value;
        }
    }
}

使い方サンプル

class Program
{
    static void Main()
    {
        // プレイヤー生成
        var p = new Player();

        // スコアを加算してみる
        p.Score = 150;
        Console.WriteLine($"現在のスコア: {p.Score}");   // → 150

        // 間違ってマイナスを入れても…
        p.Score = -42;                                  // set 内で 0 に補正
        Console.WriteLine($"現在のスコア: {p.Score}");   // → 0
    }
}

実行するとどうなる?

現在のスコア: 150
現在のスコア: 0
  • p.Score = -42; を書いても、プロパティの set ブロック で自動的に 0 に補正されるため※バグの混入を未然に防げる。
  • 呼び出し側は スコアが負にならない という前提でロジックを書けるため、コード全体の安全性が向上 する。

3-4. 読み取り専用プロパティ

public class Player
{
    private readonly string _id = Guid.NewGuid().ToString();

    public string Id => _id;     // 式形式 (C#6 以降)
}

4. コーディング規約のポイント(C# の慣例)

項目推奨スタイル
フィールド名先頭小文字のキャメルケース + 前に _ を付ける例:_health
プロパティ名先頭大文字のパスカルケース例:Health
公開範囲原則:フィールドは private、外部公開はプロパティで

5. 使い分け早見表

こう思ったら…選択肢
「ただの内部データで外に見せない」private フィールド
「外部に読ませたい/書かせたい」プロパティ
「外部に読ませるだけ」get のみプロパティ
「セット時に制限・通知したい」バリデーション付きプロパティ
「全く制御不要、PoC やスクリプトで簡単に」(Unity の public フィールドなど、例外的に許容)

6. 例題:年齢を持つ Student クラス

public class Student
{
    private int _age;

    /// <summary>
    /// 0~120 以外は拒否する年齢プロパティ
    /// </summary>
    public int Age
    {
        get => _age;
        set
        {
            if (value is < 0 or > 120)
                throw new ArgumentOutOfRangeException(nameof(value), "年齢は 0~120 の範囲で指定してください。");
            _age = value;
        }
    }
}
public class Student
{
    private int _age;

    /// <summary>
    /// 0~120 以外は拒否する年齢プロパティ(従来構文版)
    /// </summary>
    public int Age
    {
        get => _age;
        set
        {
            // パターン構文 (value is < 0 or > 120) を
            // 従来の比較演算子 + 論理演算子で書き換えた例
            if (value < 0 || value > 120)
                throw new ArgumentOutOfRangeException(
                    nameof(value),
                    "年齢は 0~120 の範囲で指定してください。");

            _age = value;
        }
    }
}

変更ポイント

箇所パターン構文従来構文
範囲チェックvalue is < 0 or > 120value < 0
論理演算子or (C# 9~)
型チェック―(今回不要)

このように、パターン マッチングの relational + logical pattern は、従来構文では単純な大小比較と ||(or)で等価に書けます。

var s = new Student();
s.Age = 25;   // OK
s.Age = -1;   // 例外発生

7. 練習問題(10 分)

  1. Temperature クラスを作り、摂氏温度 Celsius をプロパティで保持しなさい。
  2. Fahrenheit 読み取り専用プロパティを追加し、華氏に変換して返しなさい。
    • ヒント:F = C * 9 / 5 + 32
  3. Celsius に −273.15 未満が設定されたら例外を出すようにしなさい。

Temperature クラス — サンプル解答

using System;

public class Temperature
{
    // ───────────────────────────────
    // 1. 摂氏温度を保持するプロパティ
    //    ・値は  -273.15 ℃ 以上でなければならない
    // ───────────────────────────────
    private double _celsius;             // 実体フィールド(private)

    public double Celsius
    {
        get => _celsius;
        set
        {
            if (value < -273.15)
                throw new ArgumentOutOfRangeException(
                    nameof(value),
                    "摂氏温度は -273.15 ℃ 以上でなければなりません。");
            _celsius = value;
        }
    }

    // ───────────────────────────────
    // 2. 華氏温度を返す読み取り専用プロパティ
    //    F = C * 9 / 5 + 32
    // ───────────────────────────────
    public double Fahrenheit => _celsius * 9 / 5 + 32;

    // ───────────────────────────────
    // 便利なコンストラクタ(任意)
    // ───────────────────────────────
    public Temperature(double celsius) => Celsius = celsius;

    public override string ToString()
        => $"{Celsius:F2} ℃ / {Fahrenheit:F2} ℉";
}

使い方例

class Program
{
    static void Main()
    {
        var t = new Temperature(25);   // 25 ℃
        Console.WriteLine(t);          // 25.00 ℃ / 77.00 ℉

        t.Celsius = -10;               // OK
        Console.WriteLine(t);          // -10.00 ℃ / 14.00 ℉

        // 下面は絶対零度以下なので例外
        t.Celsius = -300;              // throws ArgumentOutOfRangeException
    }
}

解説ポイント
  1. バリデーションCelsius の set で −273.15 未満を弾き、ArgumentOutOfRangeException を投げています。
  2. 読み取り専用プロパティFahrenheit は => 式形式で計算結果を直接返します(C# 6 以降)。
  3. 設計意図
    • フィールド _celsius はカプセル化し、直接操作させません。
    • ToString() をオーバーライドするとデバッグ時や Console.WriteLine() が便利になります。

これで 3 つの要件すべてを満たす実装となります。


8. まとめ

  • フィールド = データの実体。外には極力隠す。
  • プロパティ = フィールドへの“扉”。安全・柔軟・変更に強い。
  • まずは「外部公開=プロパティ」と覚え、必要に応じてバリデーションや読み取り専用を追加しましょう。

次のステップ

  • 自動実装プロパティの初期値設定(C# 6 の式形式)
  • readonly / init アクセサ(C# 9 以降)
  • 変更通知 (INotifyPropertyChanged) を用いた MVVM 入門

訪問数 3 回, 今日の訪問数 3回