ひとりでのアプリ開発 - fineの備忘録 -

ひとりでアプリ開発をするなかで起こったことや学んだことを書き溜めていきます

C# - プロパティ(property)、アクセサー(accessor)

初めに

 プロパティは JavaC++ にはないカプセル化をするための機能です。C#スクリプトで get, set をと書かれた文を見かけたことがあると思います。これはプロパティとよばれるものに使われるものです。本記事では、初めにアクセサーの概念を説明し、プロパティの概念とその書き方や使い方をまとめます。


事前知識(カプセル化

 プロパティの機能の説明には、オブジェクト指向で使われるカプセル化の考え方が必要になります。下の記事で説明していますので、必要があれば先にそちらをご覧ください。

fineworks-fine.hatenablog.com

アクセサー(accessor)

 アクセサーとは、

メンバ変数の値の取得や変更を行う為の関数(メソッド)

のことです。カプセル化で説明したように、変数には外部から直接アクセスせずに値を変更できる状態は、不正な値に書き換えられたり、その変数が絡んだ修正をする場合の作業量が増えたりと好ましくありません。そこで、変数に直接アクセスせずに、変数の値を取得、変更するための関数が必要になります。それがアクセサーです。

 C# でプロパティの機能を使わず、アクセサーを作るには、変数ごとに値を取得する関数、変更する関数を作る必要があります。

(例1)

class Example
{
    //実装は外部から隠蔽(privateにしておく)
    private int num;

    //値を取得するようの関数
    public int Num()
    {
        return this.num;
    }

    //値を変更するようの関数
    public void Num(int n)
    {
        this.num = n;
    }
}

(例2:点の座標から原点からの距離を求める)

/* Unity で動かすことを想定してスクリプトを書いているため、using UnityEngine や Debug.Log を使用しています。*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PropertyTest : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Value value = new Value();
        value.Axis_x(2);
        value.Axis_y(3);

        float dis = value.Distance();
        Debug.Log($"点({value.Axis_x()}, {value.Axis_y()})と原点の距離:{dis}");   
    }
}

class Value
{
    // 実装は外部から隠蔽(privateにしておく)
    private float axis_x;  //x座標
    private float axis_y;  //y座標

    public float Axis_x()  //x座標を取り出す
    {
        return this.axis_x;
    }
    public void Axis_x(float x)  //x座標を書き換え
    {
        this.axis_x = x;
    }

    public float Axis_y()  //x座標を取り出す
    {
        return this.axis_y;
    }
    public void Axis_y(float y)  //x座標を書き換え
    {
        this.axis_y = y;
    }

    public float Distance()
    {
        return Mathf.Sqrt(Mathf.Pow(axis_x, 2) + Mathf.Pow(axis_y, 2));
    }
}

 これでは、あまりに冗長です。C#では、これを簡略化させるためにプロパティとよばれる機能が用意されています。

プロパティ

プロパティとは

 プロパティとは

set、get を用いたメンバ変数の値の取得や変更を行う為の関数

のことです。つまり、set、get を使って記述を簡単にしたアクセサーがプロパティです。

プロパティの書き方

 プロパティは次のように set、get を用いて記述します。

アクセスレベル 型名 プロパティ名
{
    set
    {
        //  ここに値の変更時の処理を書く。
        //  value という名前の変数に代入された値が格納される。
    }
    get
    {
        //  ここに値の取得時の処理を書く。
        //  メソッドの場合と同様に、値はreturnキーワードを用いて返す。
    }
}

 set 内の処理を setter、get 内の処理を getter と呼びます。これらを使うことで、変数ごとに取得用、変更用の2つのアクセサーが必要だったものが、一つにまとめることができます。

(例)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PropertyTest : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Value value = new Value();
        value.Axis_x = 2;
        value.Axis_y = 3;

        float dis = value.Distance;
        Debug.Log($"点({value.Axis_x}, {value.Axis_y})と原点の距離:{dis}");   
    }
}

class Value
{
    // 実装は外部から隠蔽(privateにしておく)
    private float axis_x;  //x座標
    private float axis_y;  //y座標

    //プロパティの書き方
    public float Axis_x
    {
        set { this.axis_x = value; }
        get { return this.axis_x; }
    }

    public float Axis_y
    {
        set { this.axis_y = value; }
        get { return this.axis_y; }
    }

    public float Distance
    {
        //get のみの場合は読み取り専用
        get { return Mathf.Sqrt(Mathf.Pow(axis_x, 2) + Mathf.Pow(axis_y, 2)); }
    }
}

 プロパティには次の特徴があります、

  • set、get を用い、記述を簡略化できる
  • クラス内部からは関数のように振る舞い、クラス外部から利用する場合は変数のように振る舞う

 上の例でも、Value クラスの外である PropertyTest クラスから Value クラスの変数を取得、変更する場合に

value.Axis_x = 2; 

のように変数に代入するように記述しています。

プロパティの書き方(自動プロパティ、省略形)

 C#3.0 以降、自動プロパティの機能が使えるようになり、プロパティの get/set の中身の省略して、次のように記述できるようになりました。

アクセスレベル 型 プロパティ名{ set; get;}

(例)

public float Axis_x { get; set; }

 このように記述するとコンパイラによって、次のようなコードに相当するものが自動的に生成されます。

private float axis_x;
public float Axis_x
{
    get { return this.axis_x; }
    set { this.axis_x = value; }
}

 private float axis_x; の部分は自動で生成されるフィールドはバックフィールドと呼ばれ、プログラマが参照できるようなものではありません。

アクセス修飾子の付け方

 get、set にアクセス修飾子をつけることができます。また、C#2.0 以降から get と set に異なるアクセス修飾子をつけることができるようになりました。

(例1:省略しない場合)

private float axis_x;  //x座標

public float Axis_x
{
     protected set { this.axis_x = value; }
     get { return this.axis_x; }
}

(例2:省略形)

public float Axis_x { get; protected set; }
get-only プロパティ

 C#6.0 から get のみのプロパティである get-only プロパティを定義できるようになりました。

 get-only プロパティは次のような特徴があります。

  • 読み取り専用
  • コンストラクタでだけ値を代入可能

(例)

public class PropertyTest : MonoBehaviour
{
    void Start()
    {
        //コンストラクタで値を代入
        Value value = new Value(2.0f);
    }
}

class Value
{
    public float Axis_x { get; }

    //コンストラクタ内でだけ代入可能
    public Value(float axis_x)
    {
        Axis_x = axis_x;
    } 
}

 コンストラクタは、インスタンスを作ったタイミングで呼び出される関数で new で呼び出されるものになります。

プロパティ初期化子

 C#6.0 から自動プロパティに対して、初期化をすることができるようになりました。これにより、コンストラクタを記述しなくても、初期値を与えることができるようになりました。

public float Axis_x { get; set; } = 2.0f;

参考

ufcpp.net

最後に

 get、set は一行で書かれていることが多く、初めはよくわからなかったのですが、成り立ちが分かると関数として認識でき、理解することができました。