[C#] プロキシクラスを動的に作成する



Answers

動的オブジェクトはこの質問の中で説明さているよう使うことができますが、動的に生成され強く型付けされたオブジェクトの場合は、疑わしいようにReflection.Emitを使用する必要があります。 このブログには、タイプの動的作成とインスタンス化を示すサンプルコードがあります。

私はRoslynに動的なプロキシの作成を容易にする機能があることを読んだので、おそらくそこを見ることもできます。

Question

私はプロキシクラスを動的に作成しようとしています。 私はこれを行うためのいくつかの非常に良いフレームワークがあることを知っているが、これは純粋に学習の練習としてペットプロジェクトですので、自分でそれをやりたいです。

たとえば、次のクラスでインターフェイスを実装しているとします。

interface IMyInterface
{
    void MyProcedure();
}

class MyClass : IMyInterface
{
    void MyProcedure()
    {
        Console.WriteLine("Hello World");
    }
}

このクラスのメソッドをインターセプトしてログに記録するには、同じインターフェースを実装するが、実際のクラスへの参照を含む別のクラス(プロキシクラスの私のバージョン)を作成しています。 このクラスは、アクション(例:ロギング)を実行してから、実際のクラスで同じメソッドを呼び出します。

例えば:

class ProxyClass : IMyInterface
{
    private IMyInterface RealClass { get; set; }

    void MyProcedure()
    {
        // Log the call
        Console.WriteLine("Logging..");

        // Call the 'real' method
        RealClass.MyProcedure();
    }
}

次に、呼び出し側は代わりにプロキシクラスのすべてのメソッドを呼び出します(実際のクラスの代わりにプロキシクラスを挿入するための基本的な自家製IoCコンテナを使用しています)。 RealClassを実行時に同じインタフェースを実装する別のクラスに交換できるようにしたいので、このメソッドを使用しています。

実行時にProxyClassを作成し、 RealClassプロパティを設定して実際のクラスのプロキシとして使用できるようにする方法はありますか? これを行う簡単な方法はありますか、 Reflection.Emitようなものを使用してMSILを生成する必要がありますか?




私はこれを行うことをお勧めしません。 通常、CastleやEntLibなどのよく知られたライブラリを使用します。 複雑なクラスの中には、プロキシを動的に生成するのがかなり難しいものがあります。 ここでは、 "Is"多型を使ってそれを行う例を示します。 このためには、すべてのメソッドを仮想として宣言しなければなりません。 あなたがこれをやろうとしていたやり方(「あります」)も可能ですが、私にとってはもっと複雑に見えます。

public class A
{
    public virtual void B()
    {
        Console.WriteLine("Original method was called.");
    }
}

class Program
{

    static void Main(string[] args)
    {
        // Create simple assembly to hold our proxy
        AssemblyName assemblyName = new AssemblyName();
        assemblyName.Name = "DynamicORMapper";
        AppDomain thisDomain = Thread.GetDomain();
        var asmBuilder = thisDomain.DefineDynamicAssembly(assemblyName,
                     AssemblyBuilderAccess.Run);

        var modBuilder = asmBuilder.DefineDynamicModule(
                     asmBuilder.GetName().Name, false);

        // Create a proxy type
        TypeBuilder typeBuilder = modBuilder.DefineType("ProxyA",
           TypeAttributes.Public |
           TypeAttributes.Class |
           TypeAttributes.AutoClass |
           TypeAttributes.AnsiClass |
           TypeAttributes.BeforeFieldInit |
           TypeAttributes.AutoLayout,
           typeof(A));
        MethodBuilder methodBuilder = typeBuilder.DefineMethod("B", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.ReuseSlot);
        typeBuilder.DefineMethodOverride(methodBuilder, typeof(A).GetMethod("B"));


        // Generate a Console.Writeline() and base.B() calls.
        ILGenerator ilGenerator = methodBuilder.GetILGenerator();
        ilGenerator.Emit(OpCodes.Ldarg_0);
        ilGenerator.EmitWriteLine("We caught an invoke! B method was called.");

        ilGenerator.EmitCall(OpCodes.Call, typeBuilder.BaseType.GetMethod("B"), new Type[0]);
        ilGenerator.Emit(OpCodes.Ret);

        //Create a type and casting it to A. 
        Type type = typeBuilder.CreateType();
        A a = (A) Activator.CreateInstance(type);

        // Test it
        a.B();
        Console.ReadLine();
    }
}



Links