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的方式指定属性。
浙公网安备 33010602011771号