たとえば以下のような ref
パラメータを持ったメソッドがあったとして、
private static void ShaderErrorListUI(Shader shader, ShaderMessage[] messages, ref Vector2 scrollPosition) { // ... }
これをリフレクション経由で呼びたい場合は、多分普通に作るとこうなる。
var t = typeof(typeof(Editor).Assembly.GetType("UnityEditor.ShaderInspector)); var m = t.GetMethod("ShaderErrorListUI", BindingFlags.NonPublic | BindingFlags.Static); m.Invoke(null, new object[] { shader, messages, scrollPosition });
ただ、これだとパフォーマンス的にあまりよくないので、式木を使ってこんな感じにするとする。
var args = Expression.Parameter(typeof(object[]), "args"); var body = m.GetParameters().Select((w, i) => Expression.Convert(Expression.ArrayIndex(args, Expression.Constant(i)), w.ParameterType)).Cast<Expression>().ToArray(); var call = Expression.Call(null, m, body); var invoke = Expression.Lambda<Action<object[]>>(body, args).Compile(); invoke.Invoke(new object[] { shader, messages, scrollPosition });
こうすると式木は 1 度だけコンパイルされるのでリフレクションよりは早く動く......のだけど、実は呼び出し時エラーになる。
System.ArgumentException: Type must not be ByRef (Parameter 'type')
というのも、実は ref
みたいな引数にパラメータが付いている場合は、 T
に対して MakeByRefType()
が呼ばれたメソッドなので、キャストできない。
ということで、以下のように ParameterType
の所に、下記のようなメソッドを挟んであげると良い。
private static Type UnpackType(Type t) { if (t.IsByRef) return t.GetElementType(); return t; }
ということで、メモでした。