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

后端 未结 5 1256
渐次进展
渐次进展 2020-12-13 16:07

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

相关标签:
5条回答
  • 2020-12-13 16:42

    If anyone is interested in the performance trade-off between the simple reflection approach (also nice examples here and here) and Marc's Expression-building approach...

    My test involved getting a relatively deep property (A.B.C.D.E) 10,000 times.

    1. Simple reflection: 64 ms
    2. Expression-building: 1684 ms

    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.

    0 讨论(0)
  • 2020-12-13 16:53

    You want to look at providing your own PropertyDescriptor's via TypeConverter or some other source.

    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.

    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.

    I am using this quite successfully with Linq2SQL objects and in combination with their binding lists :)

    0 讨论(0)
  • 2020-12-13 16:55

    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]!='\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]);
    }
    
    0 讨论(0)
  • 2020-12-13 17:01

    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
    }
    
    0 讨论(0)
  • 2020-12-13 17:03

    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.

    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).

    (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();
        }
    }
    
    0 讨论(0)
提交回复
热议问题