Как создать дерево выражений /lambda для глубокого свойства из строки
С учетом строки: "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
Почему вы не используете рекурсию? Что-то вроде:
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
}
Ответ 2
Похоже, что вы отсортированы с регулярным отражением, но для информации код для создания выражения для вложенных свойств будет очень похож на этот порядок- по коду.
Обратите внимание, что для установки значения вам нужно использовать 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();
}
}
Ответ 3
Если кто-то заинтересован в компромиссе производительности между простым отражением (также хорошие примеры здесь и здесь) и Marc Выражение выражений...
Мой тест включал получение относительно глубокого свойства (A.B.C.D.E) 10 000 раз.
- Простое отражение: 64 мс
- Выражение выражений: 1684 мс
Очевидно, это очень специфический тест, и я не рассматривал оптимизацию или настройки свойств, но я думаю, что стоит отметить 26-кратное повышение производительности.
Ответ 4
Вы хотите посмотреть на предоставление собственного PropertyDescriptor через TypeConverter или какой-либо другой источник.
Я реализовал именно то, что вы описываете для текущего проекта (извините, коммерческий, иначе я бы поделился), путем получения BindingSource и предоставления информации через него.
Идея такова:
Все, что вам нужно сделать, - это когда вы создаете небольшие "стеки" для getter и seters свойств, и те, которые вы можете собрать, пройдя сначала дерево свойств этого типа и его свойства, ограничивая глубины до заданного количества уровней и удаление круговых ссылок в зависимости от ваших структур данных.
Я использую это довольно успешно с объектами 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]);
}