40

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

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

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 を生成する必要がありますか?

4

4 に答える 4

1

これを行うことはお勧めしません。通常、Castle や EntLib などの有名なライブラリを使用します。一部の複雑なクラスでは、プロキシを動的に生成するのは非常に困難な場合があります。「Is」ポリモーフィズムを使用してそれを行う例を次に示します。このためには、base のすべてのメソッドを virtual として宣言する必要があります。あなたがこれをやろうとしていた方法(「持っている」)も可能ですが、私にとってはもっと複雑に見えます。

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();
    }
}
于 2013-03-31T21:20:32.487 に答える
1

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

Roslynには動的プロキシの作成を容易にする機能があることを読んだので、そちらも参照してください。

于 2013-03-31T21:22:04.767 に答える