Динамическое создание прокси-classа

Я пытаюсь создать проксиclass динамически. Я знаю, что есть очень хорошие frameworks для этого, но это чисто любимый проект в качестве учебного упражнения, поэтому я хотел бы сделать это сам.

Если, например, у меня есть следующий class, реализующий интерфейс:

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

Чтобы перехватить методы для этого classа для их регистрации, я создаю еще один class (моя версия прокси-classа), который реализует один и тот же интерфейс, но содержит ссылку на «настоящий» class. Этот class выполняет действие (например, протоколирование), а затем вызывает тот же метод в реальном classе.

Например:

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

Затем вызывающий вызов вызывает все методы в прокси-classе (я использую основной контейнер IoC для домашнего использования, чтобы ввести class прокси вместо реального classа). Я использую этот метод, потому что я хотел бы иметь возможность менять RealClass во время выполнения для другого classа, реализующего один и тот же интерфейс.

Есть ли способ создать ProxyClass во время выполнения и заполнить его свойство RealClass чтобы он мог использоваться как прокси для реального classа? Есть ли простой способ сделать это или мне нужно использовать что-то вроде Reflection.Emit и генерировать MSIL?

Посмотрите System.Runtime.Remoting.Proxies.RealProxy . Вы можете использовать это, чтобы создать экземпляр, который является целевым типом с точки зрения вызывающего. RealProxy.Invoke предоставляет точку, из которой вы можете просто вызвать целевой метод для базового типа или выполнить дополнительную обработку до / после вызова (например, ведение журнала).

Вот пример прокси-сервера, который регистрируется на консоли до / после каждого вызова метода:

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

Вот как вы его используете:

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

Результатом вывода на консоль будет:

Перед вызовом: MyProcedure
Привет мир
После вызова: MyProcedure

Вы можете использовать динамические объекты, как описано в этом вопросе , но для динамически генерируемого строго типизированного объекта вам придется использовать Reflection.Emit , как вы подозревали. В этом блоге есть пример кода, показывающий динамическое создание и создание экземпляра типа.

Я прочитал, что у Roslyn есть функции, которые упрощают создание динамических прокси, поэтому, возможно, посмотрите там тоже.

Возможно, я неправильно понял вопрос, как насчет конструктора?

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

Я бы не рекомендовал это делать. Обычно вы используете некоторые известные библиотеки, такие как Castle или EntLib. Для некоторых сложных classов может быть довольно сложной задачей динамически генерировать прокси. Вот пример того, как использовать polymorphism «Is». Для этого вы должны объявить все свои методы в базе виртуальными. То, как вы пытались это сделать («Has»), также возможно, но для меня выглядит более сложным.

 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(); } } 
Давайте будем гением компьютера.