我正在努力理解为什么需要像PostSharp这样的后编译器?
我的理解是它只是在原始代码中插入代码,那么为什么开发人员不自己编写代码呢?
我希望有人会说它更容易编写,因为您可以在方法上使用属性,然后不会将它们弄乱样板代码,但这可以使用 DI 或反射和一点预谋来完成,而无需后编译器。我知道,既然我已经说过反射,那么性能大象现在将进入 - 但我不关心这里的相对性能,因为大多数场景的绝对性能是微不足道的(亚毫秒到毫秒)。
让我们尝试对这个问题提出一个架构观点。假设您是一名架构师(每个人都想成为一名架构师;)您需要将架构交付给您的团队:一组选定的库、架构模式和设计模式。作为您设计的一部分,您说:“我们将使用以下设计模式实现缓存:”
string key = string.Format("[{0}].MyMethod({1},{2})", this, param1, param2 );
T value;
if ( !cache.TryGetValue( key, out value ) )
{
using ( cache.Lock(key) )
{
if (!cache.TryGetValue( key, out value ) )
{
// Do the real job here and store the value into variable 'value'.
cache.Add( key, value );
}
}
}
这是进行跟踪的正确方法。开发人员将实现这种模式数千次,因此您编写了一个很好的 Word 文档,说明您希望如何实现该模式。是的,一个Word文档。你有更好的解决方案吗?恐怕你不会。经典代码生成器无济于事。函数式编程(委托)?它在某些方面工作得相当好,但不是在这里:您需要将方法参数传递给模式。那么还剩下什么?用自然语言描述模式并相信开发人员会实现它们。
会发生什么?
首先,一些初级开发人员会查看代码并告诉“嗯。两个缓存查找。有点没用。一个就足够了。” (这不是开玩笑——向 DNN 团队询问这个问题)。而且您的模式不再是线程安全的。
作为架构师,您如何确保正确应用该模式?单元测试?很公平,但是您很难以这种方式检测到线程问题。代码审查?这也许就是解决方案。
现在,你决定改变什么模式?例如,您检测到缓存组件中的错误并决定使用您自己的?您要编辑数千种方法吗?这不仅仅是重构:如果新组件具有不同的语义怎么办?
如果您决定不再缓存某个方法怎么办?删除缓存代码有多难?
与纯代码相比,AOP 解决方案(无论框架是什么)具有以下优点:
所以如果你把它们放在一起:
我有一个 90 分钟的关于这个主题的演讲,你可以在http://vimeo.com/2116491观看。
同样,AOP 的架构优势与您选择的框架无关。框架之间的差异(也在本视频中讨论过)主要影响您可以将 AOP 应用于代码的程度,这不是这个问题的重点。
假设您已经有一个设计良好、经过良好测试等的类。您想轻松地在某些方法上添加一些时间。是的,您可以使用依赖注入,创建一个装饰器类,它代理原始但每个方法的时间 - 但即使是那个类也会是一团糟......
...或者您可以将反射添加到混合中并使用某些描述的动态代理,这使您可以编写一次计时代码,但需要您正确获取该反射代码-这并不像想象的那么容易,尤其是在涉及泛型的情况下。
...或者您可以为要计时的每个方法添加一个属性,编写一次计时代码,并将其作为后编译步骤应用。
我知道哪个对我来说似乎更优雅 - 在阅读代码时更明显。它甚至可以应用于不适合 DI 的情况(而且它确实不适用于系统中的每个类)并且在其他地方没有其他更改。
AOP (PostSharp) 用于将代码从一个位置附加到应用程序中的各种点,因此您不必将其放置在那里。
您无法使用反射实现 PostSharp 可以做的事情。
我个人认为它在生产系统中没有太大用处,因为大多数事情都可以通过其他更好的方式(日志记录等)来完成。
您可能想查看有关此问题的其他线程:
Aspects 消除了所有的复制和粘贴代码,让添加新功能的速度更快。
例如,我讨厌一遍又一遍地编写相同的代码。Gael 在他的网站 (www.postsharp.net) 上有一个关于 INotifyPropertyChanged 的非常好的示例。
这正是 AOP 的用途。忘记技术细节,只需执行您被要求的内容。
从长远来看,我认为我们都应该告别我们现在编写软件的方式。编写样板代码并手动迭代是乏味且明显愚蠢的。
未来属于由面向对象框架结合在一起的声明式、函数式风格——以及由切面处理的横切关注点。
我想唯一不会很快得到它的人是那些仍然为代码行付费的人。