как создать дерево выражений / лямбда-выражение для глубинного свойства из строки

StackOverflow https://stackoverflow.com/questions/536932

  •  22-08-2019
  •  | 
  •  

Вопрос

Задана строка:"Person.Адрес.Почтовый индекс" Я хочу иметь возможность получить / установить это свойство почтового индекса для экземпляра 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; }
}
Это было полезно?

Решение

Почему вы не используете рекурсию?Что - то вроде:

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
}

Другие советы

Звучит так, как будто вы отсортированы с помощью обычного отражения, но для информации код для построения выражения для вложенных свойств был бы очень похож на этот порядок-по коду.

Обратите внимание, что для установки значения вам необходимо использовать 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<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();
    }
}

Если кто-то заинтересован в компромиссе производительности между простое отражение подход (также хорошие примеры здесь и здесь) и Марка Построение выражения подходите...

Мой тест включал в себя получение относительно глубокого свойства (A.B.C.D.E) 10 000 раз.

  1. Простое отражение:64 мс
  2. Построение выражения:1684 мс

Очевидно, что это очень специфический тест, и я не рассматривал оптимизацию или настройку свойств, но я думаю, что стоит отметить увеличение производительности в 26 раз.

Вы хотите рассмотреть возможность предоставления вашего собственного propertydescriptor'а через TypeConverter или какой-либо другой источник.

Я реализовал именно то, что вы описали для текущего проекта (извините, коммерческого, иначе я бы поделился), используя BindingSource и предоставляя информацию через него.

Идея заключается в следующем:

Все, что вам нужно сделать, как только у вас будет тип, - это создать небольшие "стеки" для средств получения и установки свойств, которые вы можете собрать, пройдя сначала по дереву свойств типа и его свойствам в ширину, ограничив глубину указанным количеством уровней и удалив циклические ссылки в зависимости от ваших структур данных.

Я довольно успешно использую это с объектами 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]=a-b;
                    break;
                case '*':ch[--j]=a*b;
                    break;
                case '/':ch[--j]=a/b;
                    break;
            }
        }
    }
    printf("\nValue = %d",ch[0]);
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top