トーフメモ

主にゲーム制作

3秒でわかる「リフレクション」【UnityC#】

f:id:tofgame:20190428235503j:plain

リフレクションとは

メタデータを使ってクラスやメソッドの情報にアクセスすることです!

メタデータは、クラス名やメソッド名、変数などの、プログラムデータに関する情報のことです。

リフレクションを使うことで、メタデータの取得はもちろん、メタデータからクラスや関数を呼び出すこともできます。

アセンブリからデータを呼び出すこともできます!すごい!

リフレクションプローブ(Reflection Prove)とは無関係です。

今回は、他クラスのメタデータの取得とメソッドの呼び出しを試してみます。

メタデータを見てみる

まずはメタデータを取得する対象のクラスを定義します。

public class TestClass {
    public int num;
    private int numPrivate;

    public void Method () {
    }

    private void MethodPrivate () {
    }
}

実際にメタデータを取得するスクリプトを書きます!

using UnityEngine;
using System.Reflection;
using System;

public class TestReflection : MonoBehaviour {
    private BindingFlags bindingFlags =
        BindingFlags.Instance |
        BindingFlags.Static |
        BindingFlags.Public |
        BindingFlags.NonPublic |
        BindingFlags.DeclaredOnly;

    private void Start () {
        Test();
    }

    private void Test () {
        Type type = Type.GetType("TestClass");

        //メンバーのデータ
        Debug.Log("-----Member-----");
        foreach(var i in type.GetMembers(bindingFlags)) {
            Debug.Log(i);
        }

        //変数のデータ
        Debug.Log("-----field-----");
        foreach(var i in type.GetFields(bindingFlags)) {
            Debug.Log(i);
        }

        //メソッドのデータ
        Debug.Log("-----Method-----");
        foreach(var i in type.GetMethods(bindingFlags)) {
            Debug.Log(i);
        }
    }
}

ここでのポイント

リフレクションをするために含めます。
using System.Reflection;
using System;
「TestClass」クラスを取得します。
Type type = Type.GetType("TestClass");
これらのメソッドでメタデータを取得します。
type.GetMembers();
type.GetFields();
type.GetMethods();
type.Get~系のメソッドの引数に入れることで、アクセスする対象を決めます。
private BindingFlags bindingFlags =
        BindingFlags.Instance |
        BindingFlags.Static |
        BindingFlags.Public |
        BindingFlags.NonPublic |
        BindingFlags.DeclaredOnly;

「Instance」「Static」はどちらかがなければいけなく、また、「Public」「NonPublic」もどちらかがなければいけません。
「Instance」「Static」「Public」「NonPublic」は名前から意味が分かると思います。
「DeclaredOnly」は対象にするクラスのみをアクセスする、という意味です。

BindingFlagsについて詳しく知りたい方は以下のページを見てください!
docs.microsoft.com

実行する

f:id:tofgame:20190511013422p:plain
うまく取れていそうですね!
メンバーの.ctor()はコンストラクタです!
次はメソッドを実際に呼び出してみます。

メソッドを呼び出してみる

他クラスからSumメソッドを呼び出して足し算をしてみます!

TestClassを書き換える

Sumメソッドを定義します。

public class TestClass {
    public int Sum (int a,int b) {
        return a + b;
    }
}

TestReflectionを書き換える

TestClassのSum()メソッドをSum()メソッドでラップします!

using UnityEngine;
using System.Reflection;
using System;

public class TestReflection : MonoBehaviour {
    private Type[] argType = new Type[] { typeof(Int32),typeof(Int32) };
    private BindingFlags bindingFlags =
        BindingFlags.Instance |
        BindingFlags.Static |
        BindingFlags.Public |
        BindingFlags.NonPublic |
        BindingFlags.DeclaredOnly;

    private void Start () {
        Debug.Log(Sum(1,3));
    }

    private int Sum (int a,int b) {
        object[] args = { a,b };

        Type type = Type.GetType("TestClass");
        object tmp = Activator.CreateInstance(type);
        return (int)type.GetMethod("Sum",bindingFlags,null,argType,null).Invoke(tmp,args);
    }
}

ここでのポイント

「TestClass」のインスタンス生成
object tmp = Activator.CreateInstance(type);
メソッドの引数を定義
private Type[] argType = new Type[] { typeof(Int32),typeof(Int32) };
メソッドを取得してリターン
private int Sum (int a,int b) {
    object[] args = { a,b };

    Type type = Type.GetType("TestClass");
    object tmp = Activator.CreateInstance(type);
    return (int)type.GetMethod("Sum",bindingFlags,null,argType,null).Invoke(tmp,args);
}

GetMethod()を使用してTestClassのSum()メソッドを取得しています。
ここでは1、2、4個目の引数を扱っています。3、5個目はnullでOKです。

内容としては、
[1]メソッドの名前
[2]BindingFlags
[3](Binderクラス)
[4]引数の型
[5](ParameterModifier構造体)
になっています。

そして、Invoke()で関数を実行します。
引数に「TestClass」のインスタンスと実際に入れた引数を入れます。

実行する

f:id:tofgame:20190511031139p:plain
これで関数呼び出し完了です!

まとめ

・リフレクションはメタデータを使ってクラスやメソッドの情報にアクセスすること
・GetMember()などで他クラスのメタデータを取得できる
Invoke()で実行

参考にしました

docs.microsoft.com