2009-02-11 12 views
11

Podano ciąg znaków: "Person.Address.Postcode" Chcę móc uzyskać/ustawić tę właściwość kodu pocztowego na wystąpieniu osoby. Jak mogę to zrobić? Mój pomysł polegał na podzieleniu łańcucha przez "." a następnie iteracyjne nad częściami, patrząc na posesji na poprzednim typie, a następnie zbudować drzewo wyrażenie, które mogłoby wyglądać podobnie jak (przepraszam za składni pseudo)jak utworzyć drzewo wyrażeń/lambda dla głębokiej właściwości z ciągu znaków

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

Mam poważne kłopoty acutally tworzenie drzewo wyrażeń! Jeśli jest to najlepszy sposób, czy ktoś może zasugerować, jak to zrobić, czy istnieje łatwiejsza alternatywa?

Dzięki

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

Odpowiedz

2

Dlaczego nie użyć rekurencji? Coś jak:

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

Zawsze staram się przerobić rzeczy. nie komplikuj! chory, spróbuj tego, ta –

+0

Pamiętaj, że C# nie jest rekursywny, więc możesz skończyć z wyjątkiem StackOverflow. –

21

Brzmi jakbyś klasyfikowane ze zwykłej refleksji, ale dla informacji, kod zbudować wyrażenie właściwości zagnieżdżonych byłaby bardzo podobna do this order-by code.

Należy pamiętać, że aby ustawić wartość, należy użyć GetSetMethod() na właściwości i wywołać to - nie ma wbudowanego wyrażenia dla przypisania wartości po zakończeniu budowy (chociaż jest to supported in 4.0).

(edit) tak:

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

właśnie szukałem na podobnym temacie, a to pojawiło się jako pierwsze w google, i odpowiedział na moje pytanie: D –

+0

Działa dobrze - ale jeśli wydajność jest problemem, zobacz http://stackoverflow.com/a/14708196/188926 – Dunc

1

Chcesz wyglądać na dostarczanie własnych PropertyDescriptor za pośrednictwem TypeConverter lub jakiegoś innego źródła.

Zaimplementowałem dokładnie to, co opisujesz w bieżącym projekcie (przepraszam, komercyjne, w przeciwnym razie chciałbym się podzielić), wywodząc z BindingSource i przekazując tam informacje.

Idea jest następująca:

Wszystko, co musisz zrobić, to skoro masz typ jest stworzenie trochę „stosy” dla getter i ustawiające właściwości, a te można zbierać poprzez chodzenie właściwość najpierw drzewo typu i jego własności, ograniczając głębokości do określonej liczby poziomów i usuwając kołowe odwołania w zależności od struktur danych.

Używam tego dość skutecznie z obiektami linq2sql iw połączeniu ze swoich list wiążących :)

-8

Expression Drzewa

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

Jeśli ktoś jest zainteresowany kompromis pomiędzy wydajnością podejścia simple reflection (także ładne przykłady here i here) i Marc'a Expression-building podejście ...

Mój test obejmował uzyskanie stosunkowo głębokiej właściwości (ABCDE) 10 000 razy.

  1. Proste odbicie: 64 ms
  2. Expression budowy: 1684 ms

Oczywiście jest to bardzo specyficzne testy, a ja nie uznały optymalizacje lub właściwości ustawienie, ale myślę, że 26x Wydajność jest warta odnotowania.

+1

Istnieje inne drogi w tym scenariuszu; a 'Func <,>' stworzony przez 'ILGenerator' może być bardzo szybki, o ile jest buforowany i nie jest ponownie tworzony dla wywołania –

+1

@Marc Zgoda, jest zdecydowanie potencjał buforowania, a ten test jest bardzo prymitywny, ponieważ jest po prostu ślepo wywołuje metodę BuildGet 1000 razy. Sądzę, że to tylko ostrzeżenie dla twórców kopii (takich jak ja!), Którzy potrzebują najszybszego rozwiązania OOTB. – Dunc

+0

Mam podobne wyniki w LinqPad - https://gist.github.com/zaus/6884806; Pomyślałem, że może to pomóc, jeśli wyrażenie nie dostanie całego 'PropertyInfo', ale nie (wygląda po prostu" czystsze ") – drzaus

Powiązane problemy