代码改变世界

Effective C# 学习笔记(四十二)理解Expression API的使用方式

2011-08-03 22:49  小郝(Kaibo Hao)  阅读(679)  评论(1编辑  收藏  举报

            .NET原来就拥有反射的机制来处理运行时的代码动态构建与使用。但这种代码的构建比较复杂,难以维护。而C#LINQDynamic支持使这个特性更加简单和容易使用。那就是使用Expression 表达式来为运行时提供动态业务逻辑。


  1. 在传输框架(WCF,Remoting,WebServices)中使用Expression表达式,来应对服务端逻辑更改。

在客户端调用WCF,Remoting,WebServices服务时,往往需要用代码生成工具生成许多代理代码,而当服务端服务的方法改变,比如新增新的方法或参数改变后,客户端的代码就面临着许多重新生成代理的工作。Expression的动态特性可以简化这些工作。

 

其原理首先是创建一个以表达式作为参数的方法,并对表达式中的参数进行Lamda表达式的转换,以保证各种传入方式(静态值、索引器、属性访问)的实现。

如下代码所示:

var client = new ClientProxy<IService>();

var result = client.CallInterface<string>(srver => srver.DoWork(172));

 

public TResult CallInterface<TResult>(Expression<Func<T, TResult>> op)

{

var exp = op.Body as MethodCallExpression;

var methodName = exp.Method.Name;

var methodInfo = exp.Method;

var allParameters = from element in exp.Arguments

select processArgument(element);//处理所有的方法参数

Console.WriteLine("Calling {0}", methodName);

foreach (var parm in allParameters)

Console.WriteLine("\tParameter type = {0}, Value = {1}", parm.Item1, parm.Item2);

return default(TResult);

}

//这个方法将参数处理为lamda表达式,以便应对方法返回值,属性调用,索引器等返回的表达式值

private Tuple<Type, object> processArgument(Expression element)

{

object argument = default(object);

LambdaExpression l = Expression.Lambda(Expression.Convert(element, element.Type));

Type parmType = l.ReturnType;

argument = l.Compile().DynamicInvoke();

return Tuple.Create(parmType, argument);//这里的lamda表达式在示例中创建为()=>172

}

 

  1. 整合多系统类似类型

假如你需要整合多个公司维护的系统的联系人对象到你的系统中,而这些系统中对于contact(联系人)类型定义了不同的名称,且拥有不同的字段,这时你需要的做的就是求同存异。下面的代码通过构建一个通用转换器,使被转换类型中与目标类型的public、实例、同名属性进行同名赋值。

        class CustomConverter<TSource, TDest>

    {

        private Func<TSource,TDest> converter;

        private void CreateConverterIfNeeded()

        {

            if (converter == null)

            {

//创建原类型的对象作为参数

                var source = Expression.Parameter(typeof(TSource),"source");

//创建目标类型的对象作为临时变量

                var dest = Expression.Variable(typeof(TDest),"dest");

 //首先,获取原类型的公有实例属性

                var assignments = from srcProp in

                                      typeof(TSource).GetProperties(

                                      BindingFlags.Public |

                                      BindingFlags.Instance)

                                  where srcProp.CanRead

   //获取与原类型的属性名称相同的公有实例属性

                                  let destProp = typeof(TDest).

                                  GetProperty(

                                  srcProp.Name,

                                  BindingFlags.Public |

                                  BindingFlags.Instance)

                                  where (destProp != null) &&(destProp.CanWrite)

  //构建对应属性赋值的投影

                                  select Expression.Assign(

                                  Expression.Property(dest, destProp),

                                  Expression.Property(source,srcProp));

                // put together the body:

                var body = new List<Expression>();

                body.Add(Expression.Assign(dest,Expression.New(typeof(TDest))));

                body.AddRange(assignments);

                body.Add(dest);

                var expr = Expression.Lambda<Func<TSource, TDest>>(

                    Expression.Block(

                        new[] { dest }, // expression parameters

                        body.ToArray() // body

                    ),

                    source  // lambda expression

                );

                var func = expr.Compile();

                converter = func;

            }

        }

    }

//测试自己构建的类型转换器

 CustomConverter<Person1, Person2> customConverter = new CustomConverter<Person1, Person2>();

            Person1 person1 = new Person1() { Username = "haokaibo", Address = "BJ", Age = 18 };

            Person2 person2 = customConverter.Converter(person1);

            Console.WriteLine(person1);

            Console.WriteLine(person2);