DynamicMethodによるクイックリフレクション

.NETの「おいしい」機能の1つ、特にC#は、Reflection-実行時にデータ型、そのプロパティ、メソッド、フィールドを操作するための豊富なクラスのセットです。

しかし、このような幅広い機能を使用するには、パフォーマンスを犠牲にする必要があります。

コードからメソッドを呼び出すことは、リフレクションを介して呼び出すよりもほぼ100倍高速です。

また、どの特定のクラスが使用され、どのメソッドが呼び出されるか、または特定のライブラリにバインドされたくない場合は、リフレクションを使用すると、アプリケーションのパフォーマンスが大幅に低下する可能性があります。



この記事では、そのような目的での動的メソッドの使用についてお話したいと思います。





呼び出すメソッドの引数の数のみを事前に知っていると仮定します。 これで十分でしょう...

メソッドを呼び出すテストクラスを作成します。



using System;



namespace AppForBlog1 {

class TestClass {

int index;

public int Index { get { return index; } set { index = value ; } }

public int UpdateIndex( int index) {

this .index = index;

return this .index * 2;

}

}

}




* This source code was highlighted with Source Code Highlighter .






何が欲しい? 新しいメソッドへのデリゲートを取得します。このメソッドは、必要なクラスのインスタンスと呼び出されたメソッドのパラメーターを入力として受け取り、呼び出されたメソッドの結果を返す必要があります。



最初にデリゲートを定義する必要があります。



public delegate object MethodDelegate( object inst, object param);







次に、メソッドを呼び出すために必要な情報を決定します:クラスの名前、メソッドの名前、メソッドのシグネチャ、およびこのメソッドが呼び出されるクラスのタイプ(以下で説明します)。



最も興味深いものに渡します。

リフレクションを使用して動的メソッドを作成するには、System.ReflectionおよびSystem.Reflection.Emitという名前空間をそれぞれ使用する必要があります。



最初に、呼び出すメソッドに関する情報を取得する必要があります。 これは通常の反射によって行います。



MethodInfo mi = _.GetMethod( _, BindingFlags.Public | BindingFlags.Instance, null , new Type[] { _ }, null );







次に、DynamicMethodクラスのインスタンスを作成する必要があります。



DynamicMethod dm = new DynamicMethod( __, _, _, ___ , new Type[] { __1__, __2__ }, __);







ここでは、クラスなしではメソッドは存在できないため、メソッドがアタッチされるowner_classのタイプを示すことを明確にする必要があります。

静的メソッドを作成するため、methodAttributes.PublicとMethodAttributes.Staticをmethod_属性で指定し、CallingConventions.Standardを呼び出しのメソッドで指定する必要があります。



次に、メソッドは何かをしなければなりません。 したがって、ILジェネレーターを作成し、それに指示を入力します。



ILGenerator gen = dm.GetILGenerator();







メソッドは静的ではないため、最初の引数はクラスのインスタンスで、その後にパラメーターが必要です。

なぜなら インスタンスとパラメーターはオブジェクトにパッケージ化されたメソッドに送られるため、それらを解凍する必要があります(ボックス化解除を実行します)。

メソッドを呼び出した後、結果をオブジェクトにパックし、呼び出し元のコードに返す必要があります。



gen.Emit(OpCodes.Ldarg_0);

gen.Emit(OpCodes.Unbox_Any, _);

gen.Emit(OpCodes.Ldarg_1);

gen.Emit(OpCodes.Unbox_Any, _);

gen.Emit(OpCodes.Callvirt, mi);

gen.Emit(OpCodes.Box, _);

gen.Emit(OpCodes.Ret);








メソッドの準備ができました。 デリゲートを取得するためだけに残ります。



MethodDelegate method = (MethodDelegate)dm.CreateDelegate( typeof (MethodDelegate));







これで、必要な回数だけこのデリゲートを使用して、必要なメソッドを呼び出すことができます。



上記をクラスに集めましょう。



using System;

using System.Reflection;

using System.Reflection.Emit;



namespace AppForBlog1 {

public class ReflectClass {

public static MethodDelegate GetMethodDelegate(Type owner, Type instance, string methodName, Type returnType, Type parameterType) {

//

MethodInfo mi = instance.GetMethod(methodName, BindingFlags.Public | BindingFlags.Instance, null , new Type[] { parameterType }, null );

if (mi == null ) return null ;

//

DynamicMethod dm = new DynamicMethod(methodName + "Wrapper" ,

MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard,

typeof ( object ), new Type[] { typeof ( object ), typeof ( object ) }, owner, false );

// IL-

ILGenerator gen = dm.GetILGenerator();

//

gen.Emit(OpCodes.Ldarg_0);

// object

gen.Emit(OpCodes.Unbox_Any, instance);

//

gen.Emit(OpCodes.Ldarg_1);

// object

gen.Emit(OpCodes.Unbox_Any, parameterType);

//

gen.Emit(OpCodes.Callvirt, mi);

// object

gen.Emit(OpCodes.Box, returnType);

//

gen.Emit(OpCodes.Ret);

//

return (MethodDelegate)dm.CreateDelegate( typeof (MethodDelegate));

}

}



public delegate object MethodDelegate( object inst, object param);

}



* This source code was highlighted with Source Code Highlighter .






さて、このクラスのパフォーマンスをチェックする時が来ました。



using System;

using System.Collections. Generic ;

using System.Linq;

using System.Text;

using NUnit.Framework;

namespace AppForBlog1 {

[TestFixture]

public class TestFixtureClass {

[Test]

public void FastReflectionTest() {

// " "

TestClass ts = new TestClass();



//

Assert.AreEqual(4, ts.UpdateIndex(2));

Assert.AreEqual(2, ts.Index);



//

MethodDelegate updateIndex = ReflectClass.GetMethodDelegate( typeof (TestFixtureClass), typeof (TestClass), "UpdateIndex" , typeof ( int ), typeof ( int ));



ts.Index = 1;

Assert.AreEqual(1, ts.Index);



//

Assert.AreEqual(16, updateIndex(ts, 8));

Assert.AreEqual(8, ts.Index);

}

}

}



* This source code was highlighted with Source Code Highlighter .






すべてうまくいきます!



実際のアプリケーションで使用する場合、タイプはtypeofを介してではなく、リフレクションを介して受け取る必要があります。 ただし、これを1回実行してから、デリゲートのみを使用する必要があります。デリゲートの呼び出し速度は、直接呼び出しよりも2倍遅いだけです。



頑張って



PSこのメソッドは、次のようにクラスプロパティに対しても機能します。 プロパティは、setとgetの2つのメソッドです。 また、プロパティ情報(PropertyInfo)から取得できます。



All Articles