解析字符串代码为委托: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/

转载于:https://www.cnblogs.com/zhucai/archive/2010/01/25/LambdaParser.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值