Creazione dynamic di una class proxy

Sto cercando di creare una class proxy in modo dinamico. So che ci sono alcune ottime strutture là fuori per fare questo, ma questo è puramente un progetto domestico come esercizio di apprendimento, quindi mi piacerebbe farlo da solo.

Se, ad esempio, ho la seguente class che implementa un’interfaccia:

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

Per intercettare i metodi di questa class per registrarli, sto creando un’altra class (la mia versione di una class proxy) che implementa la stessa interfaccia ma contiene un riferimento alla class ‘reale’. Questa class esegue un’azione (ad esempio la registrazione) e quindi chiama lo stesso metodo sulla class reale.

Per esempio:

 class ProxyClass : IMyInterface { private IMyInterface RealClass { get; set; } void MyProcedure() { // Log the call Console.WriteLine("Logging.."); // Call the 'real' method RealClass.MyProcedure(); } } 

Il chiamante chiama invece tutti i metodi sulla class proxy (sto usando un contenitore IoC di base per l’home-brew per iniettare la class proxy al posto della class reale). Sto usando questo metodo perché mi piacerebbe essere in grado di sostituire RealClass in fase di esecuzione in un’altra class che implementa la stessa interfaccia.

C’è un modo per creare ProxyClass in fase di esecuzione e popolare la sua proprietà RealClass in modo che possa essere utilizzato come proxy per la class reale? C’è un modo semplice per farlo o devo usare qualcosa come Reflection.Emit e generare l’MSIL?

Dai un’occhiata a System.Runtime.Remoting.Proxies.RealProxy . È ansible utilizzare questo per creare un’istanza che sembra essere il tipo di destinazione dalla prospettiva del chiamante. RealProxy.Invoke fornisce un punto da cui è ansible semplicemente richiamare il metodo di destinazione sul tipo sottostante o eseguire un’ulteriore elaborazione prima / dopo la chiamata (registrazione, ad esempio).

Ecco un esempio di un proxy che accede alla console prima / dopo ogni chiamata di metodo:

 public class LoggingProxy : RealProxy { private readonly T _instance; private LoggingProxy(T instance) : base(typeof(T)) { _instance = instance; } public static T Create(T instance) { return (T)new LoggingProxy(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); } } } 

Ecco come lo useresti:

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

L’output per console sarebbe quindi:

Prima di richiamare: MyProcedure
Ciao mondo
Dopo invocare: MyProcedure

Puoi usare gli oggetti dinamici come descritto in questa domanda , ma per un object generato dynamicmente e fortemente tipizzato dovrai usare Reflection.Emit , come sospetti. Questo blog ha un codice di esempio che mostra la creazione dynamic e l’istanza di un tipo.

Ho letto che Roslyn ha caratteristiche che rendono più semplice la creazione di proxy dinamici, quindi forse diamo un’occhiata anche lì.

Forse ho frainteso la domanda, che ne dici di un costruttore?

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

Non raccomanderei di farlo. Di solito si utilizzano alcune librerie note come Castle o EntLib. Per alcune classi complicate potrebbe essere una vera sfida generare dynamicmente un proxy. Ecco un esempio di come farlo usando il polimorfismo “Is”. Per questo devi dichiarare tutti i tuoi metodi in base come virtuali. Anche il modo in cui stavi cercando di farlo (“Has”), ma per me sembra più complicato.

 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 aB(); Console.ReadLine(); } }