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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 07:14:28  来源:igfitidea点击:

how to create expression tree / lambda for a deep property from a string

c#lambdaexpression-trees

提问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 次。

  1. Simple reflection: 64 ms
  2. Expression-building: 1684 ms
  1. 简单反射:64 毫秒
  2. 表达构建: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 倍的性能影响值得注意。