Aspect Oriented Programming 不仅仅是记录、报告等,如果你看看 PostSharp 的网站,你就会发现。就我个人而言,我没有做过太多静态 IL 编织,主要是动态 IL 生成来创建 AOP 拦截器,在这样做时,我主要使用它来包装和拦截来自控制容器反转的解析。
AOP 可以改进异常处理,改进跟踪,改进事务拦截。
NHibernate 例如有一种 AOP,尽管它在编译时是静态的,就简单的事件处理程序而言;但是对于引擎中的某些事件,您可以附加拦截器(也称为方面,事件是切入点等)——我使用它来注入,使用 IoC 业务实体到我的域对象中。
强大的 AOP 框架允许您进行泛化,甚至更强大的允许您在运行时进行无开销的泛化;原则上你有几种不同的方法:
(0)。 (不是真的)“预处理器”AOP 又名 C++ 中的模板,ifdefs 等
- 反射“AOP”
- 在运行时通过 Reflection.Emit 生成 IL,需要高度信任。这是 Castle 项目中 DynamicProxy2 采用的路径。 DynamicProxy2 相当不错,而且做了很多工作!此外,afaik PatternsAndPractices Policy Framework 也使用这种方法,虽然有自己的生成器,但有很多 XML。 NHibernate 依赖于 DynProx2。
- 通过使用 System.CodeDom.Compiler 在运行时进行 IL 编译 + Assembly.Load(...),然后加载您创建的程序集,需要高度信任。也可以使用任何其他编译器(如 Boo.Compiler)进行编译,因为它会创建“全局函数程序集”,您可以以“脚本化”方式调用它,但现在我们稍微脱离了 AOP。
- Profiler API(不要问我)
- 依赖运行时框架:扩展 MarshalByRef/ContextBoundObject see link 并使用 .Net 中的 remoting-infrastructure 进行 AOP,这非常复杂,并且引入了您可能不想要的依赖项。
- 后编译静态 IL-weaving、PostSharp 和 Mono.Cecil 具有 Reflection.Emit 的等价物,但是这个没有在具体子类(如果我没记错的话)中调用虚方法的错误,例如 Reflection.Emit 和将很乐意检查类似于 Assembly.ReflectionOnlyLoad 的代码,并且还允许您将 IL 操作输出到该代码中。如果您正在寻找一种相当低级的方法,这是一个很好的选择;不需要那么高的信任度。
- 在托管代码中添加扩展点,以便通过 p/invoke 对 C/C++ 进行非托管回调,但这需要考虑一下,因为异常不会愉快地跨越 m/um 内存边界(相反,它会弄乱您的应用程序) ,除非您在 Windows 中使用带有托管异常框架的 VC++/C#,否则这可能会出现严重的段错误。您可以将回调传递给 C 并将 p/invoke 从 C# 传递到 C,并且可能将回调从 C 传递到 C#,只要您在 C# 中定义委托即可。扩展点可能必须通过静态或动态 IL-weaver + 切入点来完成。
交易中的用法
查看Castle.Facilities.AutomaticTransactionManagement.TransactionFacility,了解使用 AOP 和 DynamicProxy2 的拦截能力处理事务的好方法。与 System.Transcations 和 System.EnterpriseServices 集成的事务工具是您使用分布式事务协调器(COM 组件)来管理事务。此外,内核中有多个 p/invoke 示例来处理TxF and TxR components of the Vista-kernel (aka Server 2008),它允许您在 NTFS 和注册表上使用事务,从而确保您所做的 CRUD 是 ACID,它也很好地与系统集成。用于创建嵌套事务的事务。
不变量验证中的用法
您还可以通过将一些属性附加到您的参数来将它们用于合同设计。
public void PerformOperation([NotNull, NotEmpty] string value) {
// use string
[NotNull] return new string(' ', 5); // can return with attributes as well
}
目前的问题是附加此元数据并在运行时检查它的开销。但是,您可以指定仅在使用 DEBUG 进行编译时应用约束检查方面,然后此元数据不会导致性能大幅下降。
如果您正在寻找公理证明,请查看 Sing#/Spec#,因为它更正式,并且工作由编译器完成。
注意事项
要注意的最重要的一点是,如果一个问题,即在您的方法之前或之后运行的某些代码正在改变控制流,可能会返回意外类型,返回过早或一般情况下不正常您调用的方法的意图可能会导致难以调试的错误。
此外,请注意从属性中抛出异常,因为您永远不知道何时或从哪个程序集发生反射;对您的属性的反映可能不会在您期望的时候发生。当我在属性中附加类型并仔细检查它们时,这发生在我身上。
还要注意这样一个事实,即您在添加全局“切入点”时打开了一个可能的攻击向量,如果有人可以访问该切入点,可以用来重定向系统的大部分。
其他框架
如果您有兴趣了解有关 AOP 的更多信息,我建议您查看 Rickard Öberg 在Qi4J 上的演示文稿,它是 Java 中用于 AOP 的一个非常好的框架(尽管 Java 具有稍微不同的对象继承语义,但它会让人有点难以理解)在 C#/F#/Nermle/Boo 中使用。
AOP + 插件
将面向方面编程与运行时生成的程序集(例如 dynamicproxy2 创建的程序集)一起使用的另一个有趣的可能性是,您还可以使用它们来包装跨应用程序边界的对象,从而简化插件管道的创建。我曾暗自希望微软在为 3.5 创建 AddIn 框架时会使用它,但不幸的是,他们选择采用静态代码生成方式,为开发人员创建加载项带来了相当大的开销。问题是,除非卸载完整的 AppDomain,否则无法再次卸载为“不仅仅是反射”而加载到 AppDomain 中的类型,因此您需要 1) 在不加载插件的情况下反射插件以查看它的功能,除非您允许写入或生成大量手动元数据(并相信这一点)和 2)一些对象来保存对象的句柄,这样它就不会被 GC 处理并且您不知道类型(因此 IContract 程序集和 AddInHandle 类)——这可能通过动态代理/AOP 以一种很好的方式完成。
使用 AOP 进行全局垃圾回收 ...在公共语言基础架构上运行在 linux/windows 上的分布式系统中。这篇论文有点难下载所以I uploaded it to my server所以我知道它在哪里。
后记
(如果您在 CLR 上使用非标准语言而不是 DLR IL-weaving 可能会创建不符合标准的代码。我认为对于 F# 特别有趣,因为使用 a lot - 对语言有很大好处的标准代码(元组说)——如果你想获得编译时警告,你可以用 [assembly: CLSCompliant] 标记你的程序集。)