我正在尝试动态创建代理类。我知道有一些非常好的框架可以做到这一点,但这纯粹是一个宠物项目,作为一个学习练习,所以我想自己做。
例如,如果我有以下实现接口的类:
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?
看一下System.Runtime.Remoting.Proxies.RealProxy。您可以使用它来创建一个从调用者的角度来看似乎是目标类型的实例。RealProxy。Invoke提供了一个点,您可以从这里简单地调用基础类型上的目标方法,或者在调用之前/之后执行额外的处理(例如,记录日志)。
下面是一个代理的示例,它在每个方法调用之前/之后记录到控制台:
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);
}
}
}
用法如下:
IMyInterface intf = LoggingProxy<IMyInterface>.Create(new MyClass());
intf.MyProcedure();
到控制台的输出将是:
调用前:MyProcedure
Hello World
调用后:MyProcedure
我不建议这样做。通常使用一些知名的库,如Castle或EntLib。对于一些复杂的类,动态生成代理可能是一个相当大的挑战。下面是一个使用"is"多态性的示例。为此,你必须将base中的所有方法声明为virtual。您尝试这样做的方式("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
a.B();
Console.ReadLine();
}
}
您可以使用这个问题中描述的动态对象,但是对于动态生成的强类型对象,您必须使用Reflection.Emit
,正如您所怀疑的那样。这个博客的示例代码展示了一个类型的动态创建和实例化。
我读到Roslyn有使创建动态代理更容易的功能,所以也许也可以看看那里。
也许我误解了这个问题,构造函数呢?
class ProxyClass : IMyInterface
{
public ProxyClass(IMyInterface someInterface)
{
RealClass = someInterface;
}
// Your other code...
}