怪怪 | Nothing, Everything

"有过一个发疯的时刻,有感觉的钢琴以为它是世界上仅有的一架钢琴,宇宙的全部和谐都发生在它身上." - 狄德罗
随笔 - 104, 文章 - 3, 评论 - 2004, 引用 - 44
数据加载中……

方法级AOP: 又一个补丁

 当前版本的地址如下:

http://files.cnblogs.com/guaiguai/FuncTest.zip

这个小例子可以看作方法级的AOP, 包里是一种实现方法, 利用了新的Expression, 比起生成动态代理类的做法那是清凉多了(当然, 也可以以这个为基础, 组合出复杂的方式,或者和生成动态代理类互补), 使用起来也灵活, 就是表达方式有点丑, 比如这样:

string c = "fuck";

MethodCall.Extract(() 
=> bar.DoSomething(c)).Invoke();

有返回时(上面的DoSomething是实例的方法, 下面这个是静态方法):

int a = 3;
int b = 4;

 List
<int> list1 = MethodCall.Extract(() => Foo.DoSomething(a, "hello", b)).FromCache().Invoke();


其中使用“()=>”这样的表达式写法是为了提取足够的信息, 包括方法、 实例和参数, FromCache这样的东西当然要自己扩展啦, 上面的包里有一个简单的例子; 而Invoke则触发它执行。

其实FromCache这样的Extension并不好, 如果这样扩展,那么使用时“FromCache().Log().CheckAccess().....Invoke()”这样臃肿的写法就会大量重复; 这里我有一个重构的方式, 不过想先听听大家的意见, 所以暂时卖个关子。

上面提到的重构,还有帮助组合的辅助设施,包括搭配使用的动态代理类生成器,  我暂时都不打算实现, 做全了工作量比较大, 同时很可能就又成了一个框架了。 最近一段时间,我个人对框架式的做法比较谨慎, 拿出这个轻薄短小的例子, 也是想先讨论一下。

这是跟老赵还有脑袋讨论的成果,其中Emit生成方法快速调用部分是1、2年前参照卢彦的例子改的(可以单独用到其它场合,不保证没有Bug哦)。脑袋其实不赞成用Emit干活的方式, 我个人同意脑袋的意见; 不过呢, 这个东西也不是完全没用, 放出来抛砖引玉吧。

这个解法的关键代码, 脑袋的“蛮力”解法, 以及这种解法和“蛮力”法之间的区别, 等时间多点一并分析。这个实现和其中的例子都比较草, 基本还是试验级别的,如果有问题可以给我留言。

Update:

做了几个测试, 发现对表达式可能出现的情况估计不足, 很多情况没有处理; 同时取表达式中的值使用了FieldInfo和PropertyInfo的GetValue, 如果执行次数很多, 效率不行(这个问题好解决, 也是用Emit生成快速调用的方法即可)。 大家暂时当例子看看, 我稍后会更新这些代码, 上面的包已经更新了一次, 添加了UnaryExpression作为参数的方法的支持:

jl_Block bundle = MethodCall.Extract(
    () 
=> dc.jl_Blocks.Single(
        (x) 
=> x.ForumID == s.SectionID)
).FromCache().Invoke();

看起来是不是稍微有点变态了? =)  事实上我觉得这样写还是不够爽, 如果变成这样:

jl_Block bundle = Cache(
    () 
=> dc.jl_Blocks.Single(
        (x) 
=> x.ForumID == s.SectionID)
)();

//或者这样

jl_Block bundle 
= Cache(
    () 
=> dc.jl_Blocks.Single        
)((x) 
=> x.ForumID == s.SectionID);

也许比较令人满意, 你觉得上面哪种比较好看呢?大家帮忙想想还有什么比较爽的写法吧。当然, 能不能实现就是另外一码事了 :P 另外, 已经照顾到的情况实际上还是很少的,当前版本肯定还有很多其它问题, 有闲趣的人不妨看看, 和我一起改进。

posted on 2008-06-12 23:54 怪怪 阅读(4521) 评论(19)  编辑 收藏

评论

#1楼    回复  引用  查看    

怪怪也写技术了?很少见啊,呵呵
最近也看了些AOP方面的东西,不过像你这种实现方式还没见到过,
看了一些AOP编程后怎么发现rails里面的before_filter,after_filter就是perfect的AOP
2008-06-13 00:27 | 横刀天笑      

#2楼    回复  引用  查看    

加一句:我居然是沙发,呵呵
2008-06-13 00:27 | 横刀天笑      

#3楼 [楼主]   回复  引用  查看    

@横刀天笑
现在居然还有人醒着... 你说的对, 虽然我不了解ruby, 不过动态语言玩这个, 要自然多了。

其实我感觉自己不适合写这类文章, 而且写着也不来劲; 看看干瘪瘪的文章内容就知道了。

更别提涉及的一些概念我本人是有抵触情绪的,不过用着C#也没办法不是。

毕竟折腾了好几个小时, 扔了就白费了, 拿出来看看谁正好用的着, 也许还能发挥点余热~
2008-06-13 00:34 | 怪怪      

#4楼    回复  引用  查看    

除了代码看着比较。。。以外
思路还是不错 个人愚钝 觉得还是一种动态代理的实现
Emit生成听着就有点害怕,不过和原来的实现方式做一下比较说不定会有惊喜哦~呵呵~

BTW: 怪怪辛苦啊,这么晚还在写
2008-06-13 00:35 | xwang      

#5楼 [楼主]   回复  引用  查看    

@xwang
确实我文中"动态代理"的说法, 不是完全妥当的,因为我这个最终也包装了一层; 只是这个仅仅在方法级别包装(也许是最小的粒度了), 因此可以零散的、 自由的使用; 更合适的说法, 应该是跟动态代理类相较而言。

我更新一下。

何着夜猫子这么多...

2008-06-13 00:45 | 怪怪      

#6楼    回复  引用  查看    

c#3.0還沒看熟
飄過...
2008-06-13 08:33 | 小生      

#7楼    回复  引用  查看    

string c = "fuck";
嘿嘿
2008-06-13 08:51 | xiao_p      

#8楼    回复  引用  查看    

jl_Block bundle = Cache(
() => dc.jl_Blocks.Single(
(x) => x.ForumID == s.SectionID)
)();

//或者这样

jl_Block bundle = Cache(
() => dc.jl_Blocks.Single
)((x) => x.ForumID == s.SectionID);

这样的语句,

第一次看见是在老赵发的“你善于使用匿名函数”的时候,当时就有种怪怪的感觉,好大一堆东西放在一起乱七八糟的。

不过,后来稍微看了些函数编程的东西,发现慢慢有点适应这种表达方式了。
2008-06-13 08:52 | xiao_p      

#9楼    回复  引用    

List<int> list1 = MethodCall.Extract(() => Foo.DoSomething(a, "hello", b)).FromCache().Invoke();
其实这种就如双鱼座所说了,这并不是aop.这已经是非常有目的地调用所需功能.
aop对我个人而言是具备对原来代码无入侵性(特别是代码的编写方式),这里显然满足不了要求.
其实用Emit感觉不错,因为我们不需要完全用emit完成所有aop处理的功能,只实现切入调用就可以了,可惜现在无法找到比较纯正的aop实现.
如果MS提供methodbody的il修改就好了,而这个修改允许在jit之前进行,只生效于当前appdomain
2008-06-13 09:12 | henryfan [未注册用户]

#10楼    回复  引用  查看    

以下是我的实现,不过可惜的是现在只支持接口实现这个规则.
[NClay.Services.Aspect(typeof(Foo)]
public List<int> DoSomething(int a,string name,int b)
{
List<int> value = FromCache();
if(value== null)
value = MethodContext.Invoke<List<int>>(a,name,b);
return value
}
2008-06-13 09:32 | henry      

#11楼 [楼主]   回复  引用  查看    

@henryfan
嗯, 你说的有道理, 不过, 我非要把FromCache()写在那里吗? :) 我这个例子简单到只是提供一个思路, 如何满足需求, 就看咱们怎么用啦。

至于无侵入, 在C#下,对DoSomething的实现完全无侵入就很不错了, 总不会要求virtual什么的。 调用方法的形式稍微产生点变化,我觉得在很多场景下,是个合理的交换。

当然动态代理类生成据我以前的试验, 也可以搞定virtual的问题, 但是有两点: 首先是不合法的东西基础不牢靠; 其次, 由于针对类和对象这一层次, 粒度还是太大。

很多时候不是咱们想怎么“纯正”的问题, 而是能不能, 和代价的问题。 总不能一味等着语言进化或者找到让自己心满意足的东西那一天吧, 瞎折腾折腾呗 :P

@xiao_p
火眼金睛..

@小生
其实我也不熟, 瞎玩...
2008-06-13 09:35 | 怪怪      

#12楼 [楼主]   回复  引用  查看    

@henry
你这个实现肯定比我这个强大, 不过肯定也重量级 :)

其实我搞这么个怪方法, 主要是为了看看在灵活性上可以走多远, 比如再设计一个规则, 一个配置方式, 可以替代继承然后override的做法, 可以变相实现多继承或Mixin,使用不同的规则, 还可能进行方法打包, 等等。

倒不是光为了AOP或者别的什么。 另外, 如果是类、接口层次的拦截, 我觉得除了Mixin接口, 也许做个预处理器, 在编译前搞些代码生成, 更加愉快。
2008-06-13 09:38 | 怪怪      

#13楼    回复  引用  查看    

@怪怪
针对类或接口进行拦截的粒度大?其实有时想一下我们是不是真的需要对所有东西进行aop呢?
对于我个人而言在business logic function的层面上做aop就足够了.而business logic function往往又以interface service的方式公开.
不过话也说回来每个人的设计思路都不同,所以要是MS能提供我说的那种方法就更好了,MS只是留个机会给我们在jit前对method进行基于当前appdomain的修改:)
2008-06-13 09:44 | henry      

#14楼 [楼主]   回复  引用  查看    

@henry
呃.., 这个确实是风格不同了.. 我恨不得连现在这种形式的类都没有呢, 接口为了runtime bound的使用场景, 勉勉强强接受了...

至于公开的时候, 按对方要求配置生成一下就可以了。
2008-06-13 09:48 | 怪怪      

#15楼    回复  引用  查看    

string c = "fuck";
2008-06-13 10:29 | 心悦      

#16楼    回复  引用  查看    

个人觉得使用Lambda expressions还是会把可读性给大大的破坏了,把接口的使用变复杂了。
2008-06-13 10:56 | 阿不      

#17楼 [楼主]   回复  引用  查看    

@心悦
-_-!

@阿不
能保证接口本身所代表的契约巍不变就可以了.., 不过确实, 原来python也计划取消lambda, 说是初学者不好掌握...
2008-06-13 18:14 | 怪怪      

#18楼    回复  引用  查看    

看起来比C里的函数指针语法还复杂..
C# 4.0据说还要加动态语言的功能,我还等着Spec#里的DbC部分加入C#,看来,C#成为下一个C++为期不远矣 :(
2008-06-14 12:51 | deerchao      

#19楼 [楼主]   回复  引用  查看    

@deerchao
这个, 不好下论断吧..

C++该死在那些几角旮旯和没有垃圾回收...
2008-07-18 03:58 | 怪怪      

标题  
姓名  
主页
Email (只有博主才能看到) 
验证码 *  看不清,换一张 [登录][注册]
内容(请不要发表任何与政治相关的内容)  
  登录  使用高级评论  新用户注册  返回页首  恢复上次提交      
该文被作者在 2008-06-13 03:39 编辑过


相关链接: