2009-02-11 23 views
11

Dada una cadena: "Person.Address.Postcode" Deseo poder obtener/establecer esta propiedad de código postal en una instancia de Person. ¿Cómo puedo hacer esto? Mi idea era dividir la cadena por "." y luego iterar sobre las partes, en busca de la propiedad del tipo anterior, a continuación, construir un árbol de expresión que sería algo como (disculpas por la sintaxis pseudo):cómo crear el árbol de expresiones/lambda para una propiedad profunda a partir de una cadena

(person => person.Address) address => address.Postcode 

Estoy teniendo un problema real; fuimos creando el árbol de expresión sin embargo! Si esta es la mejor manera, ¿alguien puede sugerir cómo hacerlo, o existe una alternativa más fácil?

Gracias

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; } 
} 

Respuesta

2

¿Por qué no se utiliza la recursividad? Algo así como:

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

siempre trato de sobreescribir cosas. ¡mantenlo simple! lo intentaré, ta –

+0

Solo tenga en cuenta que C# no es recursivo de la cola, por lo que podría terminar con la excepción de StackOverflow. –

21

Suena como si estuviera clasificadas con la reflexión regular, pero para información, el código para construir una expresión para propiedades anidadas sería muy similar a this order-by code.

Tenga en cuenta que para establecer un valor, debe usar GetSetMethod() en la propiedad e invocar que - no hay una expresión incorporada para asignar valores después de la construcción (aunque es supported in 4.0).

(editar), así:

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

acabo de buscar un tema similar y esto apareció primero en google, y respondió mi pregunta: D –

+0

Funciona bien, pero si el rendimiento es un problema, consulte http://stackoverflow.com/a/14708196/188926 – Dunc

1

que desea buscar a proporcionar su propio PropertyDescriptor de vía TypeConverter o alguna otra fuente.

He implementado exactamente lo que describe para el proyecto actual (lo siento, comercial, de lo contrario lo compartiría), al derivar de BindingSource, y proporcionar la información a través de allí.

La idea es la siguiente:

Todo lo que necesita hacer es, una vez que tenga el tipo es crear poco 'pilas' para el getter y setters de propiedades, y los puede obtener a través de caminar la propiedad árbol del tipo y sus propiedades amplían primero, limitando las profundidades a un número específico de niveles y eliminando referencias circulares según sus estructuras de datos.

estoy usando esta bastante éxito con objetos Linq2Sql y en combinación con sus listas de enlace :)

-8

Expresión árbol

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]); 
} 
2

Si alguien está interesado en la disyuntiva de rendimiento entre el enfoque simple reflection (también bonitos ejemplos here y here) y el enfoque de Marc Expression-building ...

Mi prueba consistió en obtener una propiedad relativamente profunda (ABCDE) 10.000 veces.

  1. reflexión simple: 64 ms
  2. de fomento de la expresión: 1684 ms

Obviamente, esto es una prueba muy específica, y no he considerado optimizaciones o configuración de las propiedades, pero creo que un 26x golpe de rendimiento es digno de mención.

+1

Hay otras rutas también en ese escenario; un 'Func <,>' creado a través de 'ILGenerator' puede ser muy rápido siempre que se guarde en caché y no se vuelva a crear por llamada –

+1

@Marc De acuerdo, definitivamente hay un potencial de almacenamiento en caché, y esta prueba es muy primitiva ya que simplemente llama al método BuildGet 1000 veces. Supongo que es solo una advertencia para los copiadores (¡como yo!) Que necesitan la solución OOTB más rápida. – Dunc

+0

Obtuve resultados similares en LinqPad - https://gist.github.com/zaus/6884806; Pensé que podría ser útil si la expresión no obtuviera todo el 'PropertyInfo', pero no fue así (solo se ve" más limpio ") – drzaus

Cuestiones relacionadas