expression的用途之任意实体类共享逻辑(c#)
.net下的expression tree研究几天了, 在网上看一些教程,上来就开始介绍什么是表达式树和lambda是什么,我觉得这完全没必要,只要学过编译原理的,哪个不知道表达式树是什么?我想下面的图谁都能看到懂。lambda也完全没必要介绍,不就是匿名函数吗?你教别人高等数学时候,难道从1+1开始?所以表达式树和lambda的概念稍微提下就可以了,没必要说太多的话。
再多说一点,网上很教程,特别是讲设计模式的,很喜欢用toy code做示例,这点我也非常不喜欢,没有任何实用价值!我认为,只要是识字的人,看过GOF有《设计模式》的人,没人不明白设计模式是是什么!问题是,在实际应用场景下用设计模式才是关键,这涉及到现实中的真实的需求,而不是那些作者主观弄出的toy 场景。别人在教程中一看就明白 了,可是到实际应用中又不知道在哪用了。所以,我要写教程,一定会根据现实真实需求来写。
expression的用途之任意实体类共享逻辑(c#),就是我在项目中的遇到的真实情况。先描述下需求:
1、有一控制逻辑,要求从实体类A中读出某一属性数据,结过逻辑处理,把结果放到实体类B中。比如
b.Property1=a.Property1;
2、实体类A要使用的属性,和实体类B属性,是事先不知道的。只在在组装逻辑时候,才能确定。
if (a.Property1>10) b.Property1=a.Property1;
因为逻辑要是供给所有需要逻辑的地方使用的,所以a类中Property1和b类中的property1在编写逻辑那个时刻,是未知的,只有调用的时候才能确定。
在没用expression之前,第一进入脑子的解决方案是使用接口,比如:
interface IDataSource { double Sourcedata1{get;set;} double Sourcedata2{get;set;} } interface IDataDest { double Destdata1{get;set;} double Destdata2{get;set;} } class mylogic() { private IDataSource DS; private IDestdata Dd; public mylogic(IDataSource ds,IDestdata2 dd) { this.DS=ds; this.Dd=dd; } public void Execute() { //dosomething if (Ds.Sourcedata1>Ds.Destdata1) Dd.Destdata1=Ds.Sourcedata2 } }
这种方案不能说不好,实际上这就是“依赖注入”,它遵守了依赖倒置原则,在大多数情况下,其实这是很好的一种解决方案。但是世界上没有“银弹”,再好的解决方案,也有缺点,它的缺点就是它的优点——依赖“接口”。世界就是充满矛盾的。对接口用依赖限制了它的使用,如果想使用这段逻辑,就必需实现接口。对于懒程序员来说,这要付出额外的工作,心里感觉很不爽!我想对接口也不依赖!
于是我想起来了《程序员的自我修养》这本书的教条:要对元数据编程,不要沉迷入具体数据。(元数据通常定义是关于数据的数据的,个人观点,元数据就是高一层次的抽象,小工,小李是具体,“人”是元数据。"人"做为一个类,类的数据是元数据)
.net上对元数据的支持就是反射了。
所以再想到的解决方案,就是反射。
class logic { private object SourceObj1; private object DestObj2; public double sourcePropertyName1{get;set;} public double destdataPropertyName1{get;set;} public (object obj1,object obj2) { this.SourceObj1=obj1; this.DestObj2=obj2; } public Execute() { //反射得到SourceObj1的 sourcePropertyName1值 //dosomthing //反射赋值给DestObj2的destdataPropertyName1 } } class user { public void Dosomthing(){ logic lg=new logic(); lg.sourcePropertyName1="p1"; lg.destdataPropertyName1="p1"; lg.Execute(); } }
这离任意实体类共用逻辑很接近了,但是还有不爽的地方,必须用属性要用字符串表示,实体类的属性是我已经知道了,为什么要用字符串,不能直接使用属性,并且让vs的智能提示减少敲键次数?这时我想起来moq就可以直接设置属性。在研究了moq 的源代码后,就有了下面的解决方案。
private static void log2test() { Data1 d1 = new Data1() { Value = 3 }; Data2 d2 = new Data2() { Age = 1 }; logic2<Data1, Data2> log = new logic2<Data1, Data2>(d1, d2); log.Source(d => d.Value); log.Dest(d => d.Age); log.Execute(); Console.WriteLine(d2.Age); } class logic2<TSource, TDest> { TSource ts; TDest td; LambdaExpression source; LambdaExpression dest; public void Source<TResult>(Expression<Func<TSource, TResult>> expr) { this.source = expr; } public void Dest<TResult>(Expression<Func<TDest, TResult>> expr) { this.dest = expr; } public logic2(TSource source, TDest dest) { this.ts = source; this.td = dest; } public void Execute() { //参数表达式,s var soureObjpara = Expression.Parameter(typeof(TSource), "s"); //从log.Source(d => d.Value)的lambda中提取d的Value的 PropertyInfo; var sourcePropInfo = getPropertyInfo(source); //构建sourObjpara的Property表达式 即:s.Value var sourePropExpr = Expression.Property(soureObjpara, sourcePropInfo); //参数表达式,d var destObjpara = Expression.Parameter(typeof(TDest), "d"); //从log.Dest(d => d.Age)的lambda中提取d的Age的 PropertyInfo; var destPropInfo = getPropertyInfo(dest); //构建destObjpara的Property表达式 即:d.Age var destPropExpr = Expression.Property(destObjpara, destPropInfo); //d.Age=s.Value var ass = Expression.Assign(destPropExpr, sourePropExpr); //上面所有表达式是表达式树中的节点,下面代表表达式树,表达式树最终编译为lambda函数 Expression<Action<TSource, TDest>> DosomthingExpr = Expression.Lambda<Action<TSource, TDest>> (ass, new ParameterExpression[] { soureObjpara, destObjpara }); DosomthingExpr.Compile()(ts, td); } private PropertyInfo getPropertyInfo( LambdaExpression lbexpr) { var m1 = (lbexpr.Body as MemberExpression).Member as System.Reflection.PropertyInfo; return m1; } } class Data1 { public int Value { get; set; } } class Data2 { public int Age { get; set; } }
这样就完全符合我的要求了。任意实体类共享逻辑,并且可以lambda的方式指定属性。
posted on 2012-11-19 12:40 freecoder 阅读(1066) 评论(1) 编辑 收藏 举报