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

    转载需注明出处:http://cnblogs.com/zhucai/

    我的微博:http://weibo.com/zhucai

  • 相关阅读:
    1295. 统计位数为偶数的数字『简单』
    1281. 整数的各位积和之差『简单』
    697. 数组的度『简单』
    748. 最短完整词『简单』
    832. 翻转图像『简单』
    1446. 连续字符『简单』
    1455. 检查单词是否为句中其他单词的前缀『简单』
    1160. 拼写单词『简单』
    1304. 和为零的N个唯一整数『简单』
    1103. 分糖果 II『简单』
  • 原文地址:https://www.cnblogs.com/zhucai/p/LambdaParser.html
Copyright © 2020-2023  润新知