しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

C# の列挙型風のクラスで partial 記述ができるようにする

※ これは 2022/01/01 時点の Unity 2021.2.7f1 の情報です

最新版では動作が異なる可能性がありますのでご注意ください

Packages 管理の自作基盤ライブラリで Assets 側にテンプレートファイルをコピーできるようにしたものの、列挙型(enum)の記述を partial によって既定部分とアプリごとに追記する部分みたいな分散記述ができなくて困った

これを何とかする

といっても公式 Microsoft Docs にやり方が載ってるのでそれを利用するだけ

Packages 側にこんな列挙型風の抽象クラスを用意して

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace MatatabiUx.Common
{
    /// <summary>
    /// 抽象列挙型クラス
    /// </summary>
    public abstract class Enumeration : IComparable
    {
        /// <summary>
        /// 名称
        /// </summary>
        public string Name { get; }

        /// <summary>
        /// ID
        /// </summary>
        public int Id { get; }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="id">ID</param>
        /// <param name="name">名称</param>
        protected Enumeration(int id, string name) => (Id, Name) = (id, name);

        /// <summary>
        /// <see cref="object.ToString()"/>
        /// </summary>
        public override string ToString() => Name;

        /// <summary>
        /// すべての列挙値を取得する
        /// </summary>
        /// <typeparam name="T">列挙型</typeparam>
        public static IEnumerable<T> GetItems<T>() where T : Enumeration =>
            typeof(T).GetFields(BindingFlags.Public |
                                BindingFlags.Static |
                                BindingFlags.FlattenHierarchy)
                     .Select(f => f.GetValue(null))
                     .Cast<T>();

        /// <summary>
        /// <see cref="object.Equals(object)"/>
        /// </summary>
        public override bool Equals(object obj)
        {
            if (obj is not Enumeration otherValue)
                return false;

            var typeMatches = GetType().Equals(obj.GetType());
            var valueMatches = Id.Equals(otherValue.Id);

            return typeMatches && valueMatches;
        }

        /// <summary>
        /// <see cref="object.GetHashCode()"/>
        /// </summary>
        public override int GetHashCode() => Id;

        /// <summary>
        /// <see cref="IComparable.CompareTo(object)"/>
        /// </summary>
        /// <param name="other">比較対象</param>
        public int CompareTo(object other) => Id.CompareTo(((Enumeration)other).Id);
    }
}

Assets 側に例えば音声指定用の列挙型を Enumeration クラスを継承して追加

using System.Collections.Generic;

namespace MatatabiUx.Common
{
    /// <summary>
    /// 音声種別
    /// </summary>
    public partial class Audios : Enumeration
    {
        /// <summary>
        /// 読み込み先パス
        /// </summary>
        public string Path { get; }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="id">ID</param>
        /// <param name="name">名称</param>
        /// <param name="path">読み込み先パス</param>
        public Audios(int id, string name, string path = null) : base(id, name)
        {
            if (string.IsNullOrWhiteSpace(path))
            {
                Path = $"Audios/Common/{name}";
            }
            else
            {
                Path = path;
            }
        }

        /// <summary>
        /// <see cref="Enumeration.GetItems{T}"/>
        /// </summary>
        public static IEnumerable<Audios> GetItems() => Enumeration.GetItems<Audios>();

        #region 既定の列挙値

        public static Audios Apply = new Audios(-1, nameof(Apply));
        
        public static Audios Cancel = new Audios(-2, nameof(Cancel));

        #endregion //既定の列挙値
    }
}

これとは別に partial で追加の列挙値を記述

共通の列挙値と追加の列挙値のソースを分ける

追加分のコードはこんな感じ

namespace MatatabiUx.Common
{
    /// <summary>
    /// 音声種別
    /// </summary>
    public partial class Audios : Enumeration
    {
        public static Audios Alert = new Audios(1, nameof(Alert));
    }
}

とりあえず下記のようなテストコードを走らせて試してみる

using MatatabiUx.Common;
using System.Linq;
using UnityEngine;

public class Test : MonoBehaviour
{
    public void Start()
    {
        UnityEngine.Debug.Log(
            $"Audios={string.Join(", ", Audios.GetItems().Select(x => x.Name).ToArray())}");
    }
}

ちゃんと分割した列挙値が全部出た

列挙値の一覧