CLR是否可以创建从对象Y继承的对象X,其中
- 对象Y的类型为Object
- Y的所有方法都可用于对象X
- 对象X的调用者认为他们看到了Y的所有对象
- 对象X屏蔽上面的方法,并在运行每个方法之前做一些"工作"
- 这项"工作"可能包括计算调用次数或一些任意代码
- 对象X应该能够针对任何对象工作(如果它不能针对Sealed对象工作也没关系)
另一种实现可能使用泛型,其中Object T<Y>
公开所包含对象Y 的所有方法、事件和属性
是。这称为代理对象。Castle DynamicProxy正是为了做到这一点而设计的。Krzysztof Koßmic制作了一个关于它的优秀教程。
它的工作原理是在运行时创建一个对象(在您的场景中为X),该对象继承自您要代理的对象(对象Y)。您提供了一个实现IInterceptor
的对象,该对象在调用某些方法时被调用——您在代理生成选项类中指定这些方法。
唯一需要注意的是,您希望拦截的方法必须是虚拟的,但您可能可以通过将要代理的对象包装在另一个提供虚拟方法的方法中来绕过这一点(尽管我猜您可能想要代理对象,因为这样做真的很痛苦)。
运行时代理的一个替代方案似乎符合您的需求(当调用Y上的方法时运行任意代码),但不是您的问题(子类化策略),那就是使用PostSharp或Afterthink等编译时解决方案的AOP。这种方法允许您将代码引入已编译程序集中的现有方法中,并可以执行任何您想要的操作。
PostSharp的工作原理是允许定义描述如何修改类的属性(方面)。您可以将这些属性放在方法、类型、程序集等上,当PostSharp作为后构建步骤运行时,它将查找这些属性并对编译的程序集执行转换。
Afterthink类似地使用属性方案来识别要应用于程序集中类型的修改。然而,Afterthink允许您在一个程序集中指定这些属性/修改,并将它们应用于另一个程序集,这样你就可以实际修改一个目标程序集,该程序集没有任何属性或对Afterthink的引用,也没有对你引入的库的引用。我在社区反馈的基础上向Afterthing添加了这个功能,希望能够在不依赖AOP工具的情况下执行优雅的转换程序集的原始源代码不再可用的情况。
这是一个使用Afterthink自动输出每个方法调用对修改类型的执行时间的示例:
public override void Amend(Method method)
{
method.Context<Stopwatch>()
.Before((T instance, string method, object[] parameters)
=> { var s = new Stopwatch(); s.Start(); return s; })
.After((T instance, string method, Stopwatch stopwatch, object[] parameters)
=> Console.WriteLine(method + ": " + stopwatch.ElapsedMilliseconds);
}
在这种情况下,修改类型中的每个方法都将在每个方法的开头创建一个System.Diagnostics.Stopwatch
实例,并在每个方法结束时写出方法的名称和执行时间。