2013-03-31 18 views
34

Próbuję dynamicznie utworzyć klasę proxy. Wiem, że istnieją pewne bardzo dobre ramy, aby to zrobić, ale jest to wyłącznie projekt dla zwierząt domowych jako ćwiczenie do nauki, więc chciałbym to zrobić sam.Dynamiczne tworzenie klasy proxy

Jeśli, na przykład, mam następujące klasy wykonawczych interfejs:

interface IMyInterface 
{ 
    void MyProcedure(); 
} 

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

przechwycić metody do tej klasy w celu ich dziennik, tworzę inną klasę (moja wersja klasy proxy) który implementuje ten sam interfejs, ale zawiera odniesienie do "prawdziwej" klasy. Ta klasa wykonuje akcję (na przykład rejestrowanie), a następnie wywołuje tę samą metodę na prawdziwej klasie.

Na przykład:

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

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

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

Dzwoniący następnie wywołuje wszystkie metody na klasę proxy zamiast (Używam podstawowy domowy napar IoC kontenera wstrzyknąć klasę proxy zamiast rzeczywistej klasie). Używam tej metody, ponieważ chciałbym móc zamienić RealClass w czasie wykonywania na inną klasę implementującą ten sam interfejs.

Czy istnieje sposób utworzenia ProxyClass w czasie wykonywania i wypełnienia jego właściwości RealClass, aby mogła być używana jako proxy dla prawdziwej klasy? Czy istnieje prosty sposób na zrobienie tego lub czy muszę użyć czegoś takiego jak Reflection.Emit i wygenerować MSIL?

+0

Co masz na myśli generować dynamicznie? Masz na myśli, że masz obiekt Type i chcesz utworzyć obiekt tego typu? – JJ15k

+0

Tak. Chciałbym utworzyć instancję klasy ProxyClass w środowisku wykonawczym z IMyInterface, zapełnić jej właściwość "RealClass" odwołaniem do prawdziwej klasy, aby można było wykorzystać wszystkie połączenia do prawdziwej klasy. Będę edytować pytanie, aby wyjaśnić dziękuję. –

+1

Zobacz DynamicProxy Castle'a http://www.castleproject.org/projects/dynamicproxy/ –

Odpowiedz

44

Spójrz na System.Runtime.Remoting.Proxies.RealProxy. Możesz użyć tego do utworzenia instancji, która wydaje się być typem docelowym z perspektywy osoby dzwoniącej. RealProxy.Invoke zapewnia punkt, z którego można po prostu wywołać metodę docelową na typie bazowym lub wykonać dodatkowe przetwarzanie przed/po wywołaniu (na przykład logowanie).

Oto przykład z prokurentem, że loguje się do konsoli przed/po każdym wywołaniu metoda:

public class LoggingProxy<T> : RealProxy 
{ 
    private readonly T _instance; 

    private LoggingProxy(T instance) 
     : base(typeof(T)) 
    { 
     _instance = instance; 
    } 

    public static T Create(T instance) 
    { 
     return (T)new LoggingProxy<T>(instance).GetTransparentProxy(); 
    } 

    public override IMessage Invoke(IMessage msg) 
    { 
     var methodCall = (IMethodCallMessage)msg; 
     var method = (MethodInfo)methodCall.MethodBase; 

     try 
     { 
      Console.WriteLine("Before invoke: " + method.Name); 
      var result = method.Invoke(_instance, methodCall.InArgs); 
      Console.WriteLine("After invoke: " + method.Name); 
      return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall); 
     } 
     catch (Exception e) 
     { 
      Console.WriteLine("Exception: " + e); 
      if (e is TargetInvocationException && e.InnerException != null) 
      { 
       return new ReturnMessage(e.InnerException, msg as IMethodCallMessage); 
      } 

      return new ReturnMessage(e, msg as IMethodCallMessage); 
     } 
    } 
} 

Oto jak można go używać:

IMyInterface intf = LoggingProxy<IMyInterface>.Create(new MyClass()); 
intf.MyProcedure(); 

wyjście do pocieszyć by następnie być:

Przed powołaniem: MyProcedure
Hello World
Po INVO ke: MyProcedure

+0

Tego właśnie szukałem i wskazał mi właściwy kierunek. Dziękuję Ci. –

+18

Ważnym ograniczeniem jest jednak to, że typ zawijania do proxy powinien pochodzić z MarshalByRefObject. – Piedone

+0

@Piedone, typ do zawijania powinien być interfejsem lub pochodzić z MarshalByRefObject – chapluck

0

Być może źle zrozumiałem pytanie, a co z konstruktorem?

class ProxyClass : IMyInterface 
{ 
    public ProxyClass(IMyInterface someInterface) 
    { 
     RealClass = someInterface; 
    } 
    // Your other code... 
} 
0

Nie polecałbym tego. Zwykle używasz dobrze znanych bibliotek, takich jak Castle lub EntLib. W przypadku niektórych skomplikowanych klas wyzwaniem może być dynamiczne generowanie proxy. Oto przykład robienia tego za pomocą polimorfizmu "Is". W tym celu musisz zadeklarować wszystkie swoje metody w bazie jako wirtualne. Sposób, w jaki próbujesz to zrobić ("Has") również jest możliwy, ale dla mnie wygląda na bardziej skomplikowany.

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(); 
    } 
} 
1

Można używać dynamicznych obiektów jak opisano in this question, ale dynamicznie generowanych silnie wpisany obiektu trzeba będzie użyć Reflection.Emit, jak podejrzewał. This blog ma przykładowy kod pokazujący dynamiczne tworzenie i tworzenie instancji typu.

Przeczytałem, że Roslyn ma funkcje, które ułatwiają tworzenie dynamicznych serwerów proxy, więc może też tam zajrzeć.

Powiązane problemy