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编辑  收藏  举报

导航