閉じる

【C#】プロパティ入門|getとsetの書き方・使い方を徹底解説

オブジェクト指向のC#では、フィールドをそのままpublicにせず、プロパティを使って安全かつ便利に値を扱うことが基本です。

本記事では、C#のプロパティの仕組みとgetsetの書き方を、初心者の方でも理解しやすいように、図解とサンプルコードを用いて詳しく解説します。

C#におけるプロパティとは

プロパティの役割とフィールドとの違い

プロパティは、クラス内部のフィールド(実際の値の保存場所)へのアクセスを制御するための仕組みです。

直接フィールドをpublicにすると、外部から何でも代入できてしまい、不正な値や想定外の状態が簡単に作られてしまいます。

そこで、通常はフィールドをprivateにし、その代わりにpublicなプロパティを用意して、そこから値の取得・設定を行います。

プロパティの中で、次のようなことができます。

  • 値を取得する前に、加工する
  • 値を設定する前に、チェックや変換を行う
  • 特定条件のときだけ設定を許可する

このように、プロパティは「安全なインターフェース」として動作します。

基本的なプロパティの書き方

手動実装プロパティの基本形

まずは、最も基本的な「普通の」プロパティの書き方です。

C#
using System;

public class Person
{
    // 実際の値を格納するフィールドはprivateにする
    private string name;

    // 外部に公開するプロパティ
    public string Name
    {
        // 値を取得するためのアクセサ
        get
        {
            // 必要ならここで加工してもよい
            return name;
        }

        // 値を設定するためのアクセサ
        set
        {
            // 必要ならここでチェックや変換が可能
            name = value;
        }
    }
}

public class Program
{
    public static void Main()
    {
        var person = new Person();

        // プロパティへ値を代入(内部的にはsetが呼び出される)
        person.Name = "Taro";

        // プロパティから値を取得(内部的にはgetが呼び出される)
        Console.WriteLine(person.Name);
    }
}
実行結果
Taro

この例では、Nameプロパティを通じてnameフィールドにアクセスしています。

person.Name = "Taro";と書くとsetアクセサが、Console.WriteLine(person.Name);と書くとgetアクセサが自動で呼び出されます。

getとsetの基本的な役割

getアクセサは、プロパティの値を読み取るときに呼び出されます。

setアクセサは、プロパティに値を代入するときに呼び出されます。

setの中ではvalueという特別なキーワードが使えます。

このvalueには、代入された値が入っています。

C#
public string Name
{
    get
    {
        return name;  // フィールドの値を返す
    }
    set
    {
        // "Nameプロパティに代入された値" が value に入ってくる
        name = value;
    }
}

プロパティを変数のように見せつつ、内部ではメソッドのように柔軟に処理を挟めるところが、最大のポイントです。

自動実装プロパティ(auto-implemented property)

コードを簡略化する省略記法

手動でフィールドとget/setを書くのは少し冗長です。

C#では「自動実装プロパティ」を使うことで、フィールド定義を省略できます。

C#
public class Person
{
    // 自動実装プロパティ
    public string Name { get; set; }
}

このように書くと、コンパイラが内部でprivate string name;のようなフィールドを自動生成し、getsetも自動的に単純なものを用意してくれます。

初期値を持つ自動実装プロパティ

自動実装プロパティには、初期値を設定することもできます。

C#
public class Person
{
    // 初期値を設定した自動実装プロパティ
    public string Name { get; set; } = "NoName";
}
C#
using System;

public class Person
{
    public string Name { get; set; } = "NoName";
}

public class Program
{
    public static void Main()
    {
        var person = new Person();

        // 何も代入していないが、初期値が入っている
        Console.WriteLine(person.Name);

        person.Name = "Hanako";
        Console.WriteLine(person.Name);
    }
}
実行結果
NoName
Hanako

特にロジックを挟まない単純なプロパティは、自動実装プロパティで書くのが一般的です。

getとsetにロジックを追加する

setで値を検証する(validation)

プロパティの利点は、代入のタイミングでチェック処理(バリデーション)を挟めることです。

年齢のように「0歳未満は許可しない」といった制約をプロパティで表現できます。

C#
using System;

public class Person
{
    private int age;

    public int Age
    {
        get
        {
            return age;
        }
        set
        {
            // 0未満の年齢は認めない
            if (value < 0)
            {
                throw new ArgumentOutOfRangeException(nameof(value), "年齢は0以上である必要があります。");
            }

            age = value;
        }
    }
}

public class Program
{
    public static void Main()
    {
        var person = new Person();

        person.Age = 20;
        Console.WriteLine(person.Age);

        try
        {
            person.Age = -5;  // 例外が発生する
        }
        catch (ArgumentOutOfRangeException ex)
        {
            Console.WriteLine("エラー: " + ex.Message);
        }
    }
}
実行結果
20
エラー: 年齢は0以上である必要があります。 (パラメーター名: value)

このように、不正な値が代入されたときに例外を投げることで、オブジェクトの一貫性を守ることができます。

getで値を加工する

getアクセサ側で、値を加工してから返すことも可能です。

C#
using System;

public class Product
{
    private int price; // 税抜き価格

    public int Price
    {
        get { return price; }
        set { price = value; }
    }

    // 読み取り専用の税込み価格プロパティ
    public int PriceWithTax
    {
        get
        {
            // 10%の消費税をかける例
            return (int)(price * 1.1);
        }
    }
}

public class Program
{
    public static void Main()
    {
        var product = new Product();
        product.Price = 1000;

        Console.WriteLine("税抜き: " + product.Price);
        Console.WriteLine("税込み: " + product.PriceWithTax);
    }
}
実行結果
税抜き: 1000
税込み: 1100

このように、内部のデータ構造はそのままに、外部から見せる形だけをプロパティで整えることができます。

読み取り専用・書き込み専用プロパティ

getだけやsetだけを持つプロパティ

プロパティはgetsetの両方を必ず持つ必要はありません。

どちらか一方だけにすることで、読み取り専用・書き込み専用を表現できます。

読み取り専用プロパティ

C#
public class Circle
{
    private double radius;

    public Circle(double radius)
    {
        this.radius = radius;
    }

    // 半径は外部から取得だけ可能
    public double Radius
    {
        get { return radius; }
    }

    // 面積を計算して返す読み取り専用プロパティ
    public double Area
    {
        get { return Math.PI * radius * radius; }
    }
}

読み取り専用にすることで、コンストラクタやクラス内部からのみ値を変更し、外部から勝手に書き換えられないようにできます。

書き込み専用プロパティ

書き込み専用はやや出番が少ないですが、例えばログ用など「設定だけ行い、値は公開しない」といった用途で使えます。

C#
using System;

public class Logger
{
    private string latestMessage;

    // 外部からはメッセージを書き込むだけ可能
    public string Message
    {
        set
        {
            latestMessage = value;
            Console.WriteLine("ログ: " + value);
        }
    }
}

アクセス修飾子でget/setの公開レベルを変える

getはpublic、setはprivateにする例

プロパティ自体はpublicだが、setだけをprivateにするといった指定もよく使われます。

C#
public class User
{
    // 外部からは読み取りのみ、クラス内部からは設定可能
    public string Id { get; private set; }

    public User(string id)
    {
        Id = id;
    }
}

この例では、Idプロパティは外部から読むことはできますが、書き換えることはできません。

「一度決めたら変えてはいけないIDだが、コードの都合上プロパティとして公開したい」といった場面で有用です。

setをinternalにするなど、細かい制御も可能

同様にinternalprotectedを使えば、アセンブリ内だけ、継承したクラスからだけ、などアクセスレベルを柔軟にコントロールできます。

C#
public string Name { get; internal set; }

式形式プロパティ(式-bodied property)

1行で書けるシンプルなプロパティ

C# 6以降では、1行で書ける「式形式プロパティ」も導入されています。

getだけのプロパティで、特に処理が単純な場合に便利です。

C#
public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    // フルネームを結合して返す読み取り専用プロパティ
    public string FullName => FirstName + " " + LastName;
}

従来の書き方だと次のようになりますが、式形式プロパティなら1行でスッキリ書けます。

C#
public string FullName
{
    get { return FirstName + " " + LastName; }
}

get/setを使うときによくある疑問

フィールドをpublicにするのと何が違うのか

フィールドをpublicにすれば、確かにコードは短くなります。

しかし、一度publicフィールドとして公開してしまうと、あとから簡単に仕様を変えられないという問題があります。

例えば、最初は単純な整数として公開していたAgeに、「0以上しか認めない」チェックを追加したくなった場合を考えてみます。

  • publicフィールドの場合
    • 途中からプロパティに変えると、外部コードのソースを書き換えないとコンパイルエラーになる可能性がある
  • 最初からプロパティにしておけば
    • クラスの外から見える形(プロパティのシグネチャ)を変えずに、中身のロジックだけを差し替えられる

このため、C#ではデータを直接publicフィールドにせず、プロパティ経由で扱うことが推奨されています。

プロパティとメソッドはどう使い分けるのか

「値を返すだけならメソッドでもよいのでは?」という疑問もよくあります。

一般的には、「状態(値)へのアクセス」はプロパティ、「処理・動作」はメソッドで表現します。

  • プロパティのイメージ
    • person.Age
    • 「その人の年齢という性質」を表す
  • メソッドのイメージ
    • person.CalculateAgeByBirthday()
    • 「誕生日から年齢を計算する処理」を表す

プロパティはあくまで「値」にアクセスするための構文的な糖衣であり、重い処理や長時間かかる処理はメソッドにする方がよいとされています。

まとめ

プロパティは、C#でクラスのデータを安全かつ柔軟に公開するための基本的な仕組みです。

getsetを使うことで、フィールドへのアクセスにチェックや変換処理を挟んだり、読み取り専用や書き込み専用にしたりと、用途に応じた制御が可能になります。

自動実装プロパティや式形式プロパティを活用すれば、コードを簡潔に保ちながらも、必要なときには手動実装に切り替え、バリデーションやロジックを追加できます。

日常的なクラス設計では、publicフィールドではなくプロパティで公開するという原則を意識しながら、getとsetを使いこなしていきましょう。

メンバ
  • プロパティ入門|getとsetの書き方・使い方(1/1)

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!