C# 如何从字符串为深层属性创建表达式树/ lambda
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/536932/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
how to create expression tree / lambda for a deep property from a string
提问by Andrew Bullock
Given a string: "Person.Address.Postcode" I want to be able to get/set this postcode property on an instance of Person. How can I do this? My idea was to split the string by "." and then iterate over the parts, looking for the property on the previous type, then build up an expression tree that would look something like (apologies for the pseudo syntax):
给定一个字符串:“Person.Address.Postcode”,我希望能够在 Person 的实例上获取/设置此邮政编码属性。我怎样才能做到这一点?我的想法是用“.”分割字符串。然后迭代这些部分,寻找前一个类型的属性,然后构建一个看起来像这样的表达式树(对伪语法表示歉意):
(person => person.Address) address => address.Postcode
I'm having real trouble acutally creating the expression tree though! If this is the best way, can someone suggest how to go about it, or is there an easier alternative?
不过,我在创建表达式树时遇到了真正的麻烦!如果这是最好的方法,有人可以建议如何去做,还是有更简单的选择?
Thanks
谢谢
Andrew
安德鲁
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; }
}
采纳答案by Konstantin Savelev
Why you don't use recursion? Something like:
为什么不使用递归?就像是:
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
}
回答by Marc Gravell
It sounds like you're sorted with regular reflection, but for info, the code to build an expression for nested properties would be very similar to this order-by code.
听起来您使用常规反射进行排序,但是对于信息,为嵌套属性构建表达式的代码与此 order-by 代码非常相似。
Note that to set a value, you need to use GetSetMethod()
on the property and invoke that - there is no inbuilt expression for assigning values after construction (although it is supported in 4.0).
请注意,要设置一个值,您需要GetSetMethod()
在属性上使用并调用它 - 没有用于在构造后分配值的内置表达式(尽管它在 4.0 中受支持)。
(edit) like so:
(编辑)像这样:
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();
}
}
回答by leppie
You want to look at providing your own PropertyDescriptor's via TypeConverter or some other source.
您想查看通过 TypeConverter 或其他一些来源提供您自己的 PropertyDescriptor。
I have implemented exactly what you describe for current project (sorry, commercial, else I would share), by deriving from BindingSource, and providing the information via there.
通过从 BindingSource 派生并通过那里提供信息,我已经完全实现了您为当前项目(抱歉,商业,否则我会分享)所描述的内容。
The idea is as follows:
思路如下:
All you need to do is, once you have the type is to create little 'stacks' for the getter and setters of properties, and those you can collect via walking the property tree of the type and its properties breadth first, limiting the depths to a specified number of levels and removing circular references depending on your data structures.
您需要做的就是,一旦您拥有该类型,就为属性的 getter 和 setter 创建小“堆栈”,您可以通过首先遍历该类型的属性树及其属性广度来收集这些堆栈,将深度限制为指定数量的级别并根据您的数据结构删除循环引用。
I am using this quite successfully with Linq2SQL objects and in combination with their binding lists :)
我在 Linq2SQL 对象和它们的绑定列表中非常成功地使用了它:)
回答by leppie
Expression Tree
表达式树
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]!='##代码##';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]!='##代码##';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]);
}
回答by Dunc
If anyone is interested in the performance trade-off between the simple reflectionapproach (also nice examples hereand here) and Marc's Expression-buildingapproach...
如果有人对简单反射方法(此处和此处也是不错的示例)和 Marc 的表达式构建方法之间的性能权衡感兴趣...
My test involved getting a relatively deep property (A.B.C.D.E) 10,000 times.
我的测试涉及获得相对较深的属性 (ABCDE) 10,000 次。
- Simple reflection: 64 ms
- Expression-building: 1684 ms
- 简单反射:64 毫秒
- 表达构建:1684 毫秒
Obviously this is a very specific test, and I haven't considered optimisations or setting properties, but I think a 26x performance hit is worth noting.
显然这是一个非常具体的测试,我没有考虑优化或设置属性,但我认为 26 倍的性能影响值得注意。