如何从字符串为深层属性创建表达式树/lambda
Posted
技术标签:
【中文标题】如何从字符串为深层属性创建表达式树/lambda【英文标题】:how to create expression tree / lambda for a deep property from a string 【发布时间】: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;
【问题讨论】:
【参考方案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 效果很好 - 但如果性能是一个问题,请参阅***.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# 不是尾递归的,因此您最终可能会遇到 *** 异常。【参考方案3】:如果有人对simple reflection 方法(还有很好的例子here 和here)和Marc 的Expression-building 方法之间的性能权衡感兴趣...
我的测试涉及获得相对较深的属性 (A.B.C.D.E) 10,000 次。
-
简单反射:64 毫秒
表达构建:1684 毫秒
显然,这是一个非常具体的测试,我没有考虑过优化或设置属性,但我认为 26 倍的性能提升是值得注意的。
【讨论】:
这种情况下还有其他路线;通过ILGenerator
创建的Func<,>
可以非常快,只要它被缓存而不是每次调用都重新创建
@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]);
【讨论】:
以上是关于如何从字符串为深层属性创建表达式树/lambda的主要内容,如果未能解决你的问题,请参考以下文章
C# 表达式树 创建生成使用lambda转成表达式树~表达式树的知识详解