【Unity】【エディタ】TypeCacheを使って指定したアトリビュートが付いている型を高速に取得する

UnityのTypeCacheを使って指定したアトリビュートが付いている型を高速に取得する方法をまとめました。

Unity2020.2.7

TypeCache?

TypeCacheを使うとUnityエディタが内部的にキャッシュしている型の情報を使用して、
本来リフレクションで行う以下の処理を高速に実行することができます。

  • 指定したアトリビュートが付いているプロパティを取得
  • 指定したアトリビュートが付いているフィールドを取得(2020.1a19から)
  • 指定したアトリビュートが付いている型を取得
  • 指定した型を継承またはまたはインターフェースを実装している型を取得

また、TypeCacheはリフレクションと比べて非常に簡潔に記述できることも特徴です。
例えばTypeCacheで指定したアトリビュートが付いている型を取得するには以下のように書きます。

var types = TypeCache.GetTypesWithAttribute<FooAttribute>();

これをリフレクションで書くには以下のように書かなければいけません。

private static IEnumerable<Type> GetTypesWithAttribute<T>() where T : Attribute
{
    var types = new List<Type>();
    var assemblies = AppDomain.CurrentDomain.GetAssemblies();
    foreach (var assembly in assemblies)
    {
        Type[] allAssemblyTypes;
        try
        {
            allAssemblyTypes = assembly.GetTypes();
        }
        catch (ReflectionTypeLoadException e)
        {
            allAssemblyTypes = e.Types;
        }

        var typesInAssembly =
            allAssemblyTypes.Where(t => !t.IsAbstract && Attribute.IsDefined(t, typeof(T), true));
        types.AddRange(typesInAssembly);
    }
    return types;
}

TypeCacheを使ったほうが非常に簡単に書けることがわかります。

APIとUnityバージョン

TypeCacheクラスの各APIは以下の通りです。

// 指定したアトリビュートが付いているプロパティを取得
TypeCache.GetMethodsWithAttribute<FooAttribute>();

// 指定したアトリビュートが付いているフィールドを取得(2020.1a19から)
TypeCache.GetFieldsWithAttribute<FooAttribute>();

// 指定したアトリビュートが付いている型を取得
TypeCache.GetTypesWithAttribute<FooAttribute>();

// 指定した型を継承またはまたはインターフェースを実装している型を取得
TypeCache.GetTypesDerivedFrom<FooAttribute>();

これらはUnity2019.2以降であれば使えますが、GetFieldsWithAttributeだけは2020.1a19から実装されたものとなります。
マニュアルには書いてなかったりしてあまりあてにならないので、このあたり情報はフォーラムで追うのがよさそうです。

forum.unity.com

ランタイムの可能性

さてTypeCacheは今のところエディタ用の機能となりますが、上記のフォーラムにはプレイヤー(ビルド)でも使えるようにする議論されています。
現状Unity社内でも議論はされバックログに積まれてはいるが実装はされていない、というステータスのようです。

参考

docs.unity3d.com

forum.unity.com