如何从字符串为深属性创建表达式树/ lambda

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

(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; } } 

为什么不使用递归? 就像是:

 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 } 

听起来你是用常规reflection排序的,但是对于info,构建嵌套属性表达式的代码与这个order-by代码非常相似。

请注意,要设置值,您需要在属性上使用GetSetMethod()并调用它 – 在构造之后没有用于分配值的内置表达式(尽管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("Bar.Name"); var getValue = BuildGet("Bar.Name"); setValue(foo, "abc"); Console.WriteLine(getValue(foo)); } static Action BuildSet(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>(expr, arg, valArg).Compile(); } static Func BuildGet(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>(expr, arg).Compile(); } } 

如果有人对简单反思方法( 这里和这里也是很好的例子)和Marc的表达式构建方法之间的性能权衡感兴趣……

我的测试涉及获得相对较深的属性(ABCDE)10,000次。

  1. 简单reflection:64毫秒
  2. 表达式构建:1684毫秒

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

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

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

想法如下:

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

我正在使用它成功地使用Linq2SQL对象并结合它们的绑定列表:)

表达树

 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]=ab; break; case '*':ch[--j]=a*b; break; case '/':ch[--j]=a/b; break; } } } printf("\nValue = %d",ch[0]); }