17

我是面向方面编程思想的新手,但我想探索在我的项目中使用它来处理日志记录、报告等的想法。为此,我有一些问题:

  • 为了这些有限的目的,我是否应该费心探索这条 AOP 路径?
  • 有哪些支持 AOP 的 .NET Framework 可用?
  • 这些框架中的哪一个支持流畅的界面(我讨厌 XML 配置):)
4

5 回答 5

23

Aspect Oriented Programming不仅仅是记录、报告等,如果您查看 PostSharp 的网站,您就会看到。就我个人而言,我没有做太多静态 IL 编织,主要是动态 IL 生成来创建 AOP 拦截器,这样做时我主要使用它来包装和拦截来自控制容器反转的解析。

AOP可以改进异常处理,改进跟踪,改进事务拦截。

例如, NHibernate有一种 AOP,尽管它在编译时是静态的,就简单的事件处理程序而言;但是对于引擎中的某些事件,您可以附加拦截器(又名方面,事件是切入点等)——我使用它来注入,使用 IoC 业务实体到我的域对象中。

强大的 AOP 框架允许您进行泛化,甚至更强大的允许您在运行时进行无开销的泛化;原则上,您有几种不同的方法

(0)。(不是真的)“预处理器”AOP 又名 C++ 中的模板,ifdefs 等

  1. 反射“AOP”
  2. 在运行时通过 Reflection.Emit 生成 IL,需要高度信任。这是 Castle 项目中 DynamicProxy2 采用的路径。DynamicProxy2 相当不错,而且做了很多工作!此外,afaik PatternsAndPractices Policy Framework 也使用这种方法,虽然有自己的生成器,但有很多 XML。NHibernate 依赖于 DynProx2。
  3. 通过使用 System.CodeDom.Compiler 在运行时进行 IL 编译 + Assembly.Load(...),然后加载您创建的组件,需要高度信任。也可以使用任何其他编译器(如 Boo.Compiler)进行编译,因为它会创建“全局函数程序集”,您可以以“脚本化”方式调用它,但现在我们稍微脱离了 AOP。
  4. Profiler API(不要问我)
  5. 依赖运行时框架:扩展 MarshalByRef/ContextBoundObject见链接并使用 .Net 中的远程处理基础结构来做 AOP,这非常复杂,并且引入了您可能不想要的依赖项。
  6. 编译后静态 IL-weaving、PostSharp 和 Mono.Cecil 具有 Reflection.Emit 的等价物,但是这个对于像 Reflection.Emit 这样的具体子类(如果我没记错的话)中的虚拟方法调用没有错误,并且很乐意检查您的代码类似于 Assembly.ReflectionOnlyLoad 并且还允许您将 IL 操作输出到该代码中。如果您正在寻找一种相当低级的方法,这是一个很好的选择;不需要那么高的信任。
  7. 通过 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 进入内核的示例来处理Vista 内核(又名 Server 2008)的 TxF 和 TxR 组件,它们允许您在 NTFS 和注册表上使用事务,从而确保您的 CRUD做的是 ACID,它也很好地与 System.Transactions 集成以创建嵌套事务。

不变验证中的用法 您还可以通过将一些属性附加到参数来将它们用于合同设计。

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 中的类型,

使用 AOP 进行全局垃圾回收......在公共语言基础架构上运行在 linux/windows 上的分布式系统中。这篇论文有点难下载,所以我把它上传到我的服务器上,这样我就知道它在哪里了。

Post Scriptum (如果您在 CLR 上使用非标准语言而不是 DLR IL-weaving 可能会创建不符合标准的代码。我认为对于 F# 尤其有趣,因为使用大量非标准代码非常棒语言的好处(元组说)——如果你想获得编译时警告,你可以用 [assembly: CLSCompliant] 标记你的程序集。)

于 2009-02-18T03:24:45.647 回答
2

我不能说 .NET 的细节,但是 AOP 和能够将钩子附加到任意方法的更一般的想法是一种方便的技术,可以解决一些其他棘手的问题。

一个例子是按合同设计。假设您有一堆方法,您想在这些方法上应用一些常见的合同。您可以在调用每个方法之前和之后添加一些“建议”(AOP 术语),而无需将其剪切并粘贴到每个方法中。

在测试时,了解某些内部方法中发生了什么通常很有用。它被调用了多少次,也许它返回了什么。您可以添加一个方面来执行该监视,而无需将分散注意力的测试代码添加到方法本身。甚至可能无法编辑该方法的代码。

只需注意方面是如何实现的。一些实现是精心制作的预处理器,可能会使调试代码更加复杂。其他人则顺利地融入了语言。动态语言很好地处理 AOP。Perl 有用于 AOP 的Aspect.pm和更通用的Hook::LexWrap来完成方法挂钩。

于 2009-02-18T03:09:12.440 回答
1

AOP 对我来说也很有趣。在我看来,日志记录和性能监控、报告是 AOP 旨在处理的内容。对于 .NET,Post Sharp是一个非常好的 AOP 框架。

我只对它进行了一点试验,但它似乎实现得很好。

于 2009-02-18T02:55:28.963 回答
1

如果您要查看 Post Sharp,您可以从 CodePlex 下载 Google Book Downloader。我认为这个项目使用它。

于 2009-02-18T03:07:00.433 回答
0

不要认为它完全不同。AOP 通过减少耦合、增加内聚力、通过赋予特定类型 1 单一职责的对象来分离关注点来改进(IMO)您的设计。如果您来自 .net 世界 PostSharp 使用自定义属性来编织建议。如果您来自 Java 世界,您可以使用名为AspectJ的 Java 扩展。AOP 的应用程序比您通常看到的要多。

于 2009-02-18T03:04:05.637 回答