解析字符串代码为委托:LambdaParser的更新

前篇:LambdaParser:解析字符串代码为Lambda表达式并编译为委托

 

最近更新
1.支持生成非强类型的委托。
2.可传入默认实例。在代码中可直接访问默认实例的成员,不需要变量名。

匿名类型的属性访问
若匿名类型仅在局部使用,是可以直接访问其属性的。但若想让其跨函数呢(比如将获取数据的部分封装成一个方法,返回匿名类型)?
不想因此多维护一个新数据类。于是只能通过object类型传递(参数或返回值),但在接受方无法还原其类型,因为类型没有名字。
于是接受方无法直接访问其属性。只能借助反射。不太方便。
C#4.0有个dynamic关键字,可解决此问题:
  dynamic obj = GetOneData(); // 假如GetOneData()返回一个匿名类型,且此类型有一个属性Id
  Console.WriteLine(obj.Id); // 这里便可以直接访问其属性Id。如果obj没有Id属性,会在运行时抛出异常
由于LambdaParser的目的就是在运行时编译代码,于是,写了个扩展方法E()来支持类似行为(当然也可以利用反射实现此方法):
  //using Zhucai.LambdaParser.ObjectDynamicExtension;
  object obj = new { Name = "zhangsan", Id = 18 }; // [obj]对象通常是通过方法调用的返回值
  int result = obj.E<int>("Id"); // result = 18
可在扩展方法E()中编写复杂代码:
  string day = (string)obj.E("CreateDate.Day.ToString(\"00\")"); // 假设obj有属性CreateDate(类型为DateTime)


 

LambdaParser的两个使用例子
1.来源于老赵的文章:《这下没理由嫌Eval的性能差了吧?》,其中有利用Expression动态创建委托的代码:

 

// target: (object)((({TargetType})instance).{Property})

// preparing parameter, object type
ParameterExpression instance = Expression.Parameter(
typeof(object), "instance");

// ({TargetType})instance
Expression instanceCast = Expression.Convert(
instance, propertyInfo.ReflectedType);

// (({TargetType})instance).{Property}
Expression propertyAccess = Expression.Property(
instanceCast, propertyInfo);

// (object)((({TargetType})instance).{Property})
UnaryExpression castPropertyValue = Expression.Convert(
propertyAccess,
typeof(object));

// Lambda expression
Expression<Func<object, object>> lambda =
Expression.Lambda
<Func<object, object>>(
castPropertyValue, instance);

this.m_getter = lambda.Compile();

 

 

利用LambdaParser改写后的等价代码:

 

// target: (object)((({TargetType})instance).{Property})
string code = string.Format("m=>(object)((({0})m).{1})", propertyInfo.ReflectedType.FullName,propertyInfo.Name);
this.m_getter = ExpressionParser.Compile<Func<object, object>>(code);

 

 

 

2.来源于装配脑袋的文章:《Expression Tree 上手指南 (三)》。其中利用Expression动态构造委托的代码:

 

//获得事件响应程序的委托类型
var delegateType = targetEvent.EventHandlerType;

//这个委托的Invoke方法有我们所需的签名信息
MethodInfo invokeMethod = delegateType.GetMethod("Invoke");

//按照这个委托制作所需要的参数
ParameterInfo[] parameters = invokeMethod.GetParameters();
ParameterExpression[] paramsExp
= new ParameterExpression[parameters.Length];
Expression[] argsArrayExp
= new Expression[parameters.Length];

//参数一个个转成object类型。有些本身即是object,管他呢……
for (int i = 0; i < parameters.Length; i++)
{
paramsExp[i]
= Expression.Parameter(parameters[i].ParameterType, parameters[i].Name);
argsArrayExp[i]
= Expression.Convert(paramsExp[i], typeof(Object));
}

//调用我们的GeneralHandler
MethodInfo executeMethod = typeof(GeneralEventHandling).GetMethod(
"GeneralHandler", BindingFlags.Static | BindingFlags.NonPublic);

Expression lambdaBodyExp
=
Expression.Call(
null, executeMethod, Expression.NewArrayInit(typeof(Object), argsArrayExp));

//如果有返回值,那么将返回值转换成委托要求的类型
//如果没有返回值就这样搁那里就成了
if (!invokeMethod.ReturnType.Equals(typeof(void)))
{
//这是有返回值的情况
lambdaBodyExp = Expression.Convert(lambdaBodyExp, invokeMethod.ReturnType);
}

//组装到一起
LambdaExpression dynamicDelegateExp = Expression.Lambda(delegateType, lambdaBodyExp, paramsExp);

//我们创建的Expression是这样的一个函数:
//(委托的参数们) => GeneralHandler(new object[] { 委托的参数们 })

//编译
Delegate dynamiceDelegate = dynamicDelegateExp.Compile();

//完成!
targetEvent.AddEventHandler(target, dynamiceDelegate);

 

 

 利用LambdaParser写这段代码:

 

//获得事件响应程序的委托类型
var delegateType = targetEvent.EventHandlerType;

//这个委托的Invoke方法有我们所需的签名信息
MethodInfo invokeMethod = delegateType.GetMethod("Invoke");
ParameterInfo[] parameters
= invokeMethod.GetParameters();

//我们创建的Expression是这样的一个函数:
//(委托的参数们) => (返回值类型)GeneralHandler(new object[] { 委托的参数们 })
string lambdaCode = string.Format("({0})=>{1}GeneralEventHandling.GeneralHandler(new object[]{{{2}}})",
string.Join(",", parameters.Select(m => m.Name).ToArray()),
invokeMethod.ReturnType.Equals(
typeof(void))?"":"("+invokeMethod.ReturnType.FullName+")",
string.Join(",", parameters.Select(m => m.Name).ToArray()));

Delegate dynamiceDelegate
= ExpressionParser.Compile(delegateType, lambdaCode, "Demo"); // 最后一个参数Demo是命名空间

//完成!
targetEvent.AddEventHandler(target, dynamiceDelegate);

 

 

 

可以看出,利用LambdaParser通过构造代码来动态生成委托(或Expression树)比直接使用Expression来构造要简单得多。

结尾
目前不支持代码块(不支持if,while,for,switch等),只支持表达式,也就是说只能写一个语句。
.NET4.0的Expression好像支持代码块了,有LoopExpression,到时应该就可以写代码块了。
过几天我讲一下解析代码的过程。
有对LINQ做部分动态查询的扩展,仿照微软发布的DynamicQuery.cs


项目主页:http://code.google.com/p/lambda-parser/

posted @ 2010-01-25 23:12  朱才  阅读(1406)  评论(1编辑  收藏  举报