【问题标题】:how to create expression tree / lambda for a deep property from a string如何从字符串为深层属性创建表达式树/lambda
【发布时间】:2010-10-06 22:06:01
【问题描述】:

给定一个字符串:“Person.Address.Postcode”,我希望能够在 Person 实例上获取/设置此邮政编码属性。我怎样才能做到这一点?我的想法是用“。”分割字符串。然后遍历这些部分,寻找前一个类型的属性,然后构建一个看起来像这样的表达式树(为伪语法道歉):

(person => person.Address) address => address.Postcode

虽然我在创建表达式树时遇到了真正的麻烦!如果这是最好的方法,有人可以建议如何去做,还是有更简单的选择?

谢谢

安德鲁

public class Person
{
    public int Age { get; set; }
    public string Name { get; set; }
    public Address Address{ get; set; }

    public Person()
    {
        Address = new Address();
    }
}

public class Address 
{
    public string Postcode { get; set; }
}

【问题讨论】:

    标签: c# lambda expression-trees


    【解决方案1】:

    听起来您是使用正则反射进行排序的,但对于信息,为嵌套属性构建表达式的代码与this order-by code 非常相似。

    请注意,要设置一个值,您需要在属性上使用 GetSetMethod() 并调用它 - 没有用于在构造后分配值的内置表达式(尽管它是 supported in 4.0)。

    (编辑)像这样:

    using System;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;
    class Foo
    {
        public Foo() { Bar = new Bar(); }
        public Bar Bar { get; private set; }
    }
    class Bar
    {
        public string Name {get;set;}
    }
    static class Program
    {
        static void Main()
        {
            Foo foo = new Foo();
            var setValue = BuildSet<Foo, string>("Bar.Name");
            var getValue = BuildGet<Foo, string>("Bar.Name");
            setValue(foo, "abc");
            Console.WriteLine(getValue(foo));        
        }
        static Action<T, TValue> BuildSet<T, TValue>(string property)
        {
            string[] props = property.Split('.');
            Type type = typeof(T);
            ParameterExpression arg = Expression.Parameter(type, "x");
            ParameterExpression valArg = Expression.Parameter(typeof(TValue), "val");
            Expression expr = arg;
            foreach (string prop in props.Take(props.Length - 1))
            {
                // use reflection (not ComponentModel) to mirror LINQ 
                PropertyInfo pi = type.GetProperty(prop);
                expr = Expression.Property(expr, pi);
                type = pi.PropertyType;
            }
            // final property set...
            PropertyInfo finalProp = type.GetProperty(props.Last());
            MethodInfo setter = finalProp.GetSetMethod();
            expr = Expression.Call(expr, setter, valArg);
            return Expression.Lambda<Action<T, TValue>>(expr, arg, valArg).Compile();        
    
        }
        static Func<T,TValue> BuildGet<T, TValue>(string property)
        {
            string[] props = property.Split('.');
            Type type = typeof(T);
            ParameterExpression arg = Expression.Parameter(type, "x");
            Expression expr = arg;
            foreach (string prop in props)
            {
                // use reflection (not ComponentModel) to mirror LINQ 
                PropertyInfo pi = type.GetProperty(prop);
                expr = Expression.Property(expr, pi);
                type = pi.PropertyType;
            }
            return Expression.Lambda<Func<T, TValue>>(expr, arg).Compile();
        }
    }
    

    【讨论】:

    • 刚刚搜索了一个类似的主题,这首先出现在谷歌上,并回答了我的问题:D
    • 效果很好 - 但如果性能是一个问题,请参阅stackoverflow.com/a/14708196/188926
    【解决方案2】:

    为什么不使用递归?比如:

    setProperyValue(obj, propertyName, value)
    {
      head, tail = propertyName.SplitByDotToHeadAndTail(); // Person.Address.Postcode => {head=Person, tail=Address.Postcode}
      if(tail.Length == 0)
        setPropertyValueUsingReflection(obj, head, value);
      else
        setPropertyValue(getPropertyValueUsingReflection(obj, head), tail, value); // recursion
    }
    

    【讨论】:

    • 我总是试图过度设计事物。把事情简单化!我试试吧,ta
    • 请记住,C# 不是尾递归的,因此您最终可能会遇到 StackOverflow 异常。
    【解决方案3】:

    如果有人对simple reflection 方法(还有很好的例子herehere)和Marc 的Expression-building 方法之间的性能权衡感兴趣...

    我的测试涉及获得相对较深的属性 (A.B.C.D.E) 10,000 次。

    1. 简单反射:64 毫秒
    2. 表达构建:1684 毫秒

    显然,这是一个非常具体的测试,我没有考虑过优化或设置属性,但我认为 26 倍的性能提升是值得注意的。

    【讨论】:

    • 这种情况下还有其他路线;通过ILGenerator 创建的Func&lt;,&gt; 可以非常快,只要它被缓存而不是每次调用都重新创建
    • @Marc 同意,肯定有缓存潜力,而且这个测试非常原始,因为它只是盲目地调用 BuildGet 方法 1000 次。我想这只是对需要最快 OOTB 解决方案的复制粘贴者(比如我!)的警告。
    • 我在 LinqPad 中得到了类似的结果 -- gist.github.com/zaus/6884806;我认为如果表达式没有得到整个 PropertyInfo 可能会有所帮助,但它没有(只是看起来“更干净”)
    【解决方案4】:

    您希望通过 TypeConverter 或其他来源提供您自己的 PropertyDescriptor。

    通过从 BindingSource 派生并通过那里提供信息,我已经完全实现了您为当前项目描述的内容(对不起,商业,否则我会分享)。

    思路如下:

    所有你需要做的是,一旦你有了类型,就是为属性的 getter 和 setter 创建小的“堆栈”,你可以通过首先遍历类型的属性树及其属性广度来收集这些堆栈,限制深度到指定数量的级别并根据您的数据结构删除循环引用。

    我在 Linq2SQL 对象以及它们的绑定列表中非常成功地使用了它:)

    【讨论】:

      【解决方案5】:

      表达式树

      struct tree
      {
          char info;
          struct tree *rchild;
          struct tree *lchild;
      };
      
      int prec(char data);
      
      typedef struct tree * node;
      
      char pop_op();
      node pop_num();
      void push_op(char item);
      
      node create()
      {
          return((node)malloc(sizeof(node)));
      }
      
      node num[20],root=NULL;
      char op[20],oprt,ev[20];
      int nt=-1,ot=-1,et=-1;
      
      main()
      {
          node newnode,item,temp;
          char str[50];
          int i,k,p,s,flag=0;
          printf("ENTER THE EXPRESSION ");
          scanf("%s",str);
          printf("\n%s",str);
          for(i=0;str[i]!='\0';i++)
          {
              if(isalnum(str[i]))
              {
                  newnode=create();
                  newnode->info=str[i];
                  newnode->lchild=NULL;
                  newnode->rchild=NULL;
                  item=newnode;
                  push_num(item);
              }
              else
              {
                  if(ot!=-1)
                      p=prec(op[ot]);
                  else
                      p=0;
                  k=prec(str[i]);
                  if(k==5)
                  {
                      while(k!=1)
                      {
                          oprt=pop_op();
                          newnode=create();
                          newnode->info=oprt;
                          newnode->rchild=pop_num();
                          newnode->lchild=pop_num();
                          // if(root==NULL)
                          root=newnode;
                          // else if((newnode->rchild==root)||(newnode->lchild==root))
                          // root=newnode;
                          push_num(root);
                          k=prec(op[ot]);
                      }
                      oprt=pop_op();
                  }
                  else if(k==1)
                      push_op(str[i]);
                  else
                  {
                      if(k>p)
                          push_op(str[i]);
                      else
                      {
                          if(k<=p)
                          {
                              oprt=pop_op();
                              newnode=create();
                              newnode->rchild=pop_num();
                              newnode->lchild=pop_num();
                              if(root==NULL)
                              root=newnode;
                              else if((newnode->rchild==root)||(newnode->lchild==root))
                              root=newnode;
                              push_num(newnode);
                              push_op(str[i]);
                              // k=prec(op[ot]);
                          }
                      }
                  }
              }
          }
          printf("\nThe prefix expression is\n ");
          preorder(root);
          printf("\nThe infix exp is\n ");
          inorder(root);
          printf("\nThe postfix expression is\n ");
          postorder(root);
          evaluate();
      }
      void push_op(char item)
      {
          op[++ot]=item;
      }
      push_num(node item)
      {
          num[++nt]=item;
      }
      char pop_op()
      {
          if(ot!=-1)
          return(op[ot--]);
          else
          return(0);
      }
      node pop_num()
      {
          if(nt!=-1)
          return(num[nt--]);
          else
          return(NULL);
      }
      int prec(char data)
      {
          switch(data)
          {
              case '(':return(1);
                  break;
              case '+':
              case '-':return(2);
                  break;
              case '*':
              case '/':return(3);
                  break;
              case '^':return(4);
                  break;
              case ')':return(5);
                  break;
          }
      }
      
      
      inorder(node temp)
      {
          if(temp!=NULL)
          {
              inorder(temp->lchild);
              printf("%c ",temp->info);
              inorder(temp->rchild);
          }
      }
      
      preorder(node temp)
      {
          if(temp!=NULL)
          {
              printf("%c ",temp->info);
              preorder(temp->lchild);
              preorder(temp->rchild);
          }
      }
      
      postorder(node temp)
      {
          if(temp!=NULL)
          {
              postorder(temp->lchild);
              postorder(temp->rchild);
              printf("%c ",temp->info);
              ev[++et]=temp->info;
          }
      }
      evaluate()
      {
          int i,j=-1,a,b,ch[20];
          for(i=0;ev[i]!='\0';i++)
          {
              if(isalnum(ev[i]))
                  ch[++j]=ev[i]-48;
              else
              {
                  b=ch[j];
                  a=ch[j-1];
                  switch(ev[i])
                  {
                      case '+':ch[--j]=a+b;
                          break;
                      case '-':ch[--j]=a-b;
                          break;
                      case '*':ch[--j]=a*b;
                          break;
                      case '/':ch[--j]=a/b;
                          break;
                  }
              }
          }
          printf("\nValue = %d",ch[0]);
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-20
        • 2013-08-17
        • 1970-01-01
        • 2019-03-12
        • 1970-01-01
        相关资源
        最近更新 更多