2009-02-11 15 views
11

Data una stringa: "Person.Address.Postcode" Voglio essere in grado di ottenere/impostare questa proprietà del codice postale su un'istanza di Persona. Come posso fare questo? La mia idea era di dividere la stringa con "." e poi scorrere sopra le parti, alla ricerca per la proprietà del tipo precedente, quindi costruire un albero di espressione che sarebbe simile (scuse per la sintassi pseudo):come creare albero di espressione/lambda per una proprietà profonda da una stringa

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

sto avendo problemi reali acutally creazione l'albero delle espressioni però! Se questo è il modo migliore, qualcuno può suggerire come procedere, oppure esiste un'alternativa più semplice?

Grazie

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

risposta

2

Perché non utilizzare la ricorsione? Qualcosa di simile:

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

cerco sempre di sovvertire le cose. mantienilo semplice! mal provato, ta –

+0

Ricorda che C# non è ricorsivo in coda, quindi potresti finire con l'eccezione StackOverflow. –

21

Sembra che tu stia ordinata con riflessione regolare, ma per informazioni, il codice per creare un'espressione per le proprietà nidificate sarebbe molto simile a this order-by code.

Si noti che per impostare un valore, è necessario utilizzare GetSetMethod() sulla proprietà e invocare che - non esiste un'espressione incorporata per l'assegnazione di valori dopo la costruzione (anche se è supported in 4.0).

(edit) in questo modo:

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

ho appena cercato su un argomento simile e questo è venuto prima su google, e ha risposto alla mia domanda: D –

+0

Funziona bene - ma se le prestazioni sono un problema, vedere http://stackoverflow.com/a/14708196/188926 – Dunc

1

si vuole guardare a fornire il proprio PropertyDescriptor di via TypeConverter o qualche altra fonte.

Ho implementato esattamente ciò che descrivi per il progetto corrente (scusate, commerciale, altrimenti condividerei), derivando da BindingSource e fornendo le informazioni via lì.

L'idea è la seguente:

Tutto quello che dovete fare è, una volta che hai il tipo è quello di creare po 'pile' per il getter e setter di proprietà, e quelli che si possono raccogliere tramite piedi la proprietà albero del tipo e le sue proprietà ampiezza prima, limitando le profondità a un numero specificato di livelli e rimuovendo riferimenti circolari a seconda delle strutture dati.

Sto usando questo con successo con gli oggetti Linq2SQL e in combinazione con le loro liste vincolanti :)

-8

Espressione Albero

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

Se qualcuno è interessato nello svolgimento trade-off tra l'approccio simple reflection (anche bei esempi here e here) e Expression-building approccio di Marc ...

Il mio test ha coinvolto ottenere una proprietà relativamente profondo (ABCDE) 10.000 volte.

  1. riflessione semplice: 64 ms
  2. Espressione di costruzione: 1684 ms

Ovviamente questo è un test molto specifico, e non ho considerato ottimizzazioni o impostazione delle proprietà, ma penso che un 26x il successo della performance è degno di nota.

+1

Ci sono altri percorsi anche in questo scenario; a 'Func <,>' creato tramite 'ILGenerator' può essere molto veloce fintanto che è memorizzato nella cache e non ricreato per chiamata –

+1

@Marc D'accordo, c'è sicuramente un potenziale di cache, e questo test è molto primitivo in quanto ciecamente chiama il metodo BuildGet migliaia di volte. Immagino sia solo un avvertimento per i copy-pasters (come me!) Che hanno bisogno della soluzione OOTB più veloce. – Dunc

+0

Ho ottenuto risultati simili in LinqPad - https://gist.github.com/zaus/6884806; Ho pensato che sarebbe stato di aiuto se l'espressione non avesse ottenuto l'intero 'PropertyInfo', ma non l'ha fatto (sembra solo" più pulito ") – drzaus

Problemi correlati