Enumを簡単にStringとしてシリアライズできるパッケージ作りました

github.comkyubuns/StringSerializableEnum

なにこれ?

こんなコードを書いてEnumをシリアライズすると、
.unityや.prefabを見ればわかるように、内部的には0, 1, 2というintの値として保存されます。

TestMonoBehaviour.cs
public class TestMonoBehaviour : MonoBehaviour
{
    [SerializeField] private TestEnum testEnum;
}

public enum TestEnum
{
    Zero,
    One,
    Two,
}

つまり、Enumの途中に値を追加/削除すると意図しない指定の変更が起きてしまいます。

TestEnum.cs
public enum TestEnum
{
    Zero,
    Half, // <- おっと、0.5も必要だった
    One,
    Two,
}

↓ "One" を指定していたはずなのに!

正攻法としては、ちゃんと手動で数値を振るということになるのですが、
Unityのシリアライザーのためにこの作業が必要になるのが気に食わない。(個人の主観です。)

TestEnum.cs
public enum TestEnum
{
    Zero = 0,
    Half = 3, // ← 後から追加した
    One = 1,
    Two = 2,
}

ということで、Stringとしてシリアライズしてくれるパッケージを作りました。

使い方

  • UnityPackageManagerで https://github.com/kyubuns/StringSerializableEnum.git?path=Unity/Assets/StringSerializableEnum をインストール。
  • StringとしてシリアライズしたいEnumに [StringSerializable] をつける。
TestEnum.cs
[StringSerializable]
public enum TestEnum
{
    One,
    Two,
    Three,
}
  • StringSerializable{Enum名} という型が自動生成されるので、それをシリアライズしてお使いください。
TestMonoBehaviour.cs
public class TestMonoBehaviour : MonoBehaviour
{
    [SerializeField] private StringSerializableTestEnum testEnum;

    public void Start()
    {
        Debug.Log($"Value is {testEnum.Value}");
    }
}

見た目は変わらず。でもズレない!

差分も見やすくて嬉しい。

Q&A

じゃあ名前を変更すると?

Inspectorではエラー表示になり、このまま実行するとランタイムで例外を吐きます。

Enumの名前が変わるのと数値が変わるの、同じぐらいのリスクじゃない?

ぶっちゃけ同じだと思ってます。ただの思想の問題。
やろうと思えば静的にチェックもできるので少しだけ嬉しいかも?

StringとEnumの変換遅くない?

以下のようにswitchに展開してるので Enum.TryParse よりは十分に早いです。
(が、高速化は専門じゃないのでもっと良い方法あれば教えてください。)

TestEnum.cs
public TestEnum Value
{
    get
    {
        if (_cached == null)
        {
            _cached = stringValue switch
            {
                "Zero" => TestEnum.Zero,
                "One" => TestEnum.One,
                "Two" => TestEnum.Two,
                _ => throw new ArgumentOutOfRangeException($"\"{stringValue}\" is not a valid value for TestEnum")
            };
        }
        return _cached.Value;
    }
}