0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Unityでconstの(ように)classの変数を使う方法

Posted at

はじめに

C#の定数について調べていると、 constよりstatic readonlyを使いましょう という記載をよく見かけますが、本当でしょうか。
本当にstatic readonlyはconstの代用になり、不変であると信じていいのでしょうか。
ダメです。static readonlyな変数の中身は書き換えることができる場合があります。

という話をしていきます。

先に結論

絶対に変わってほしくないconstのかわりにはstaticでreadonlyなpropertyをつかいましょう。

static readonlyの中身を書き換える方法

まずは実際にstatic readonlyの中身を書き換えてみましょう。

Test.cs
using UnityEngine;

namespace SandBox
{
    public class Test : MonoBehaviour
    {
        //static readonlyな変数.
        static readonly MyClass readonlyVariable = new("FIRST INPUT");

        //内部に状態を持つクラス.
        class MyClass
        {
            public string text;

            public MyClass(string input)
            {
                text = input;
            }
        }

        private void Start()
        {
            //ここではFIRST INPUTである.
            Debug.Log(readonlyVariable.text);

            //内部の状態を変えようと試みる.
            readonlyVariable.text = "NEXT INPUT";

            //ここでは...?
            Debug.Log(readonlyVariable.text);
        }
    }
}

結果は・・・
image.png
書き込みができてしまいました。これではプログラム全体を通して変数が不変とはいえません。

上の例ならわかりやすいですが、static readonlyで定数のように扱いたいList<>が外部から空にされていたら・・・?

read only(読み取り専用)とはとういうことだったのでしょうか?

「読み取り専用」は「不変であること」とは違う

readonlyを付けた変数には、代入することができなくなります。
ただし、代入を禁止しているだけで、値の普遍性を保証しているわけではないのです。

結論 : staticでreadonlyなpropertyをつかいましょう

constのように使える不変な変数が実現したければ、
static readonlyなpropertyなら値が変わることがありません。
例を見てみましょう。

Test.cs
using UnityEngine;

namespace SandBox
{
    public class Test : MonoBehaviour
    {
        //static readonlyなpropertyに変更.
        static MyClass readonlyProperty => new("FIRST INPUT");

        //内部に状態を持つクラス.
        class MyClass
        {
            public string text;

            public MyClass(string input)
            {
                text = input;
            }
        }

        private void Start()
        {
            //ここではFIRST INPUTである.
            Debug.Log(readonlyProperty.text);

            //内部の状態を変えようと試みる.
            readonlyProperty.text = "NEXT INPUT";

            //ここでは...?
            Debug.Log(readonlyProperty.text);
        }
    }
}

結果は
image.png
変わっていません!

もう少し詳しい解説

以下の2つの例では、同じように見えて実は違うふるまいをする書き方を並べました。

Test.cs
        //不変
        static MyClass ReadonlyProperty => new("FIRST INPUT");

        //不変ではない
        static MyClass GetonlyProperty { get; } = new("FIRST INPUT");

これらをそれぞれ、同じ意味のコードでわかりやすく書き換えます。

Test.cs
        //不変
        static MyClass ReadonlyProperty
        {
            get
            {
                return new("FIRST INPUT");
            }
        }

        //不変ではない
        private static readonly MyClass getonlyProperty = new("FIRST INPUT");
        static MyClass GetonlyProperty
        {
            get
            {
                return getonlyProperty;
            }
        }

上の例では、変数のように振る舞いつつも実は変数を持っておらず、毎回インスタンスを作っていることがわかります。
なので、何度ReadonlyPropertyを参照しようとも、参照するたびに新しいインスタンスが返されるため書き換えることができないのでした。

下の例ではstatic readonlyな変数をもちつつも、readonlyは不変であるわけではないため、
変数の参照を返した先で内容を書き換えることができてしまうのでした。

毎回インスタンスを生成することになるのでメモリアロケーションの面では難点がありますが、不変性が確保できるという点との天秤ですね。

0
0
12

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?