TOX Online

Enjoying coding
posts - 24, comments - 23, trackbacks - 0, articles - 0
 现在体会到什么是最痛苦的事情,刚才在Blog上直接写文章,俺一直认为这种倒霉的事情不可能发生在这个时候,结果快要结束的时候突然弹出一个错误对话框,紧接着是浏览器毫无预兆的自动关闭………………….又一次从头到脚的体会到随时保存的确是一个好习惯。

再来一次唧唧歪歪(就当刚才写的是草稿)

感觉最近大家讨论AOP的话题越来越多了,于是呢,俺也来凑个热闹,俺花了些时间,也对AOP方面琢磨了一下,这一琢磨呢,就琢磨出来一个东西,什么东西,就是AOP。目前大家见到的AOP平台一大堆,俺就不在这里列举了。并且考虑到到现在为止了解Aspect#的人们还是挺多,为了利用这一资源,俺的DProxy也是提供了类似Aspect#的接口,只有少许地方的不同。因此你如果了解Aspect#的话,那么应该对下面的内容应该比较熟悉。

虽说是轻量级,但也实现了Aspect#的两大拳头功能: Mixin Intercept
为了让所有的.NET玩家能够快速了解到什么是Mixin和Intercept,俺给出下面两个实验场景

试验场景
1:俺写了个客户管理的程序,俺想将每个操作做为日志记录下来,于是乎写了下面几行代码

public class Customer
{
    
public string Ask(string a, string b)
    
{
        Log.WriteLog(
"Customer::Ask begin");
Console.WriteLine(
"Ask: {0} or {1}", a, b));
        Log.WriteLog(
"Customer::Ask end");
        
return string.Format("I choose {0}",a));
    }

}


public class Log
{
    
public static void WriteLog(string s)
    
{
        Console.WriteLine(s);
    }

}


static void Main(string[] args)
{
    Customer c 
= new Customer();
    Console.WriteLine(c.Ask(
"red","blue"));
    Console.ReadLine();
}



上面每当ASK函数被调用的时候,在开始和结束的时候都会输出调试信息,如下图:


 但是俺总觉得这个方法有点不爽,为什么呢?如果将来Log类修改了怎么办,俺岂不是要在用过的地方全部修改一下,而且Log的代码和处理的逻辑代码放在一起总是有点别扭,怎么办?

俺用DProxy想出了一个办法

 增加一个拦截类,在Ask函数被调用的时候拦截到它,犹如很久以前的 API HOOK,哈哈

public class LogInterceptor  : IInterceptor
{
    
public object Intercept(IInvocation invocation, params object[] args)
    
{
        Log.WriteLog(
"Customer::Ask begin");
        
object ret = invocation.Proceed(args);
        Log.WriteLog(
"Customer::Ask end");
        
return ret;
    }

}

然后Ask函数就可以写为

public class Customer
{
    
public virtual string Ask(string a, string b)
{
        Console.WriteLine(
string.Format("Ask: {0} or {1}", a, b));
        
return string.Format("I choose {0}", a);
    }

}

然后将Main函数修改一下

[STAThread]
static void Main(string[] args)
{
    EmitProxyContract contract 
= new EmitProxyContract();
    contract.Target(
typeof(Customer));
contract.Intercept(
typeof(LogInterceptor),"Ask",typeof(string),typeof(string));

    EmitProxy proxy 
= (new EmitProxyFactory()).GetProxy(contract);

    Customer c 
= proxy.Wrap() as Customer;
    Console.WriteLine(c.Ask(
"red","blue"));
    Console.ReadLine();
}

Customer这个类一下子就变得清爽多了,屏幕输出依旧:


大家可以看到俺的
Customer类中的代码已经不包含任何被Log的迹象,但事实上却被Log了,这个就是Hook。由于使用了DProxy俺就能很容易的实现它。

试验场景
2:俺上面写的客户程序,俺想加一些方法进去,比如说俺要获取客户的姓名。

一般俺会这么做

public class Customer
{
    
internal string myname = “TOX”; 
    
public string GetName()
    
{
        
return "My name is " + this.myname;
    }

}

[STAThread]
static void Main(string[] args)
{
    Customer c 
= new Customer();
    Console.WriteLine(c.GetName());
    Console.ReadLine();
}

这段程序的输出是:


但是俺又想到,这个GetName可以是Customer类的,也可以是别的类的啊,那岂不是俺就要给每个需要实现GetName功能的类增加相同的代码?

俺是懒人,因此就要研究如何才能够更偷懒,之所以俺才用.NET而不用VC6。于是呢,俺又想起 DProxy,于是俺用DProxy重新写了俺的代码,如下面所示:

显示代码

看看结果如何


实现的功能是一样的哦,但是这可是把数据层和逻辑层分开了,而且代码的重用效率非常之高,用过Aspect#的人因该都知道。

无聊至极,俺用俺的DProxyAspect#做了一个测试,结果如下:






Aspect#创建的时候花的时间比较高是因为其内部有一个语言分析器,估计花了不少时间,而DProxy则没有。

因为俺不知道Aspect#动态生成的代码是啥样的,因此俺也不知道为什么Aspect#在调用的时候要花那么长的时间,DProxy生成的代理使用的都是直接调用和接口调用,估计Aspect#使用的是Invoke

关于直接调用,接口调用,Invoke等之间的性能对比可以参考MSDN的文章:http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp02172004.asp

目前DProxy尚在开发过程中,如果今后有了可供使用的版本,我会通知大家。

2006/02/13 更新:
DProxy 更名为 Sophus 并在 http://research.grapecity.com.cn/cs/files/7/sophus/default.aspx 提供免费下载

Feedback

#1楼   回复  引用  查看    

2005-11-28 14:32 by tzchw      
有启发

#2楼   回复  引用    

2005-11-28 14:37 by sdfsdf[未注册用户]
俺是湖南人?

#3楼   回复  引用    

2005-11-28 16:24 by Jifeng huang[未注册用户]
Aspect#性能比较低,主要的时间是花在重新编译一下DLL了,如果被拦截类采用Singleton性能就可以提高很多了.其它的差别不是很大.另外对Mixin概念还不是很掌握,能详细说说吗?

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

2005-11-28 17:01 by TOX      
to Jifeng huang:
Mixin 大概意思指在不修改原有类(A)代码的基础上,动态的增加函数到(A)中。当然这个是动态代理做不到的,我们其实利用了一个技巧,我们将增加的代码写到A的派生类中,而使用的时候却按照A的方法来使用。这样会感觉到A好像多了一个功能。
但是在调用的时候,我们不能用A直接调用A的派生类中增加的代码,因此我们需要预先定义一个接口,用这个接口来调用。上面代码的 contract.Mix(typeof(GetAnyName), typeof(IGetName)); 就是说将 GetAnyName 这个类实现的函数通过IGetName这个接口混合到基类 Customer 中去。
实现动态代理都需要重新编译DLL,本例使用的是 Emit。

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

2005-11-28 18:02 by TOX      
to sdfsdf:
我玩点新花样

#6楼   回复  引用    

2005-11-28 19:36 by Holy[未注册用户]
to TOX:
请问tox使用什么方法拦截的呀?我用RemotingProxy发现性能不是一个数量级的。

#7楼   回复  引用  查看    

2005-11-28 23:47 by 双鱼座      
我晕...你为什么要和Aspect#比?你只实现了DynamicProxy的功能,就只能和DynamicProxy比呀。Aspect#是对DynamicProxy的进一步抽象,完成了太多动态代理没有实现的功能。就Override方式的Proxy来讲,不太可能有比DynamicProxy使用Emit更快的方式了。
无论如何,基于动态override的Proxy是没有任何意义的,我就经常喜欢将方法定义成final,因为这样可以获得非常好的性能。RemotingProxy虽然没有很好的性能,但是通过消息机制实现,没有任何限制。

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

2005-11-29 09:41 by TOX      
to Holy:
用Emit手动构造的拦截代码,重写了原有的函数

to 双鱼座:
上面的代码已经显示了,除了定义的地方不同(Aspect用的是字符串定义,DProxy用的是对象定义),实现的功能两者是无异的,DProxy不是动态代理。
对于是否把方法定义为final这个只是个人的习惯问题,而重写override方法这个不是AOP设计的初衷,是因为目前的.NET Framework 1.1的限制,才迫于使用这个方法。


并且DProxy对Emit生成的代码具有保护功能,如果将动态生成的Assembly反编译为C#代码,就算不修改直接编译回去,会发现编译可以成功,但是代码不能正确运行。

#9楼   回复  引用  查看    

2006-11-10 22:27 by Kevin_Hon      
可以给我一份代码研究下么? Email:79961739@QQ.com 不胜感谢。。



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 285990




相关文章:

相关链接: