2009-02-11 35 views
11

Cho một chuỗi: "Person.Address.Postcode" Tôi muốn có thể nhận/đặt thuộc tính postcode này trên một cá thể của Person. Tôi có thể làm cái này như thế nào? Ý tưởng của tôi là chia chuỗi bằng "." và sau đó lặp lại qua các bộ phận, tìm kiếm các tài sản trên các loại trước đó, sau đó xây dựng lên một cây biểu hiện đó sẽ giống như thế (xin lỗi cho cú pháp giả):cách tạo biểu thức tree/lambda cho thuộc tính sâu từ một chuỗi

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

Tôi đang gặp rắc rối thực sự acutally tạo cây biểu hiện mặc dù! Nếu đây là cách tốt nhất, ai đó có thể gợi ý làm thế nào để đi về nó, hoặc là có một sự thay thế dễ dàng hơn?

Cảm ơn

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

Trả lời

2

Tại sao bạn không sử dụng đệ quy? Một cái gì đó như:

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

tôi luôn cố gắng để nghiên cứu kỹ hơn. giữ nó đơn giản! bệnh cố gắng đó, ta –

+0

Chỉ cần ghi nhớ rằng C# không phải là đệ quy đuôi, vì vậy bạn có thể kết thúc với ngoại lệ StackOverflow. –

21

Có vẻ như bạn đang sắp xếp với sự phản ánh thường xuyên, nhưng đối với thông tin, các mã để xây dựng một biểu hiện cho tính lồng nhau sẽ rất giống với this order-by code.

Lưu ý rằng để đặt giá trị, bạn cần sử dụng GetSetMethod() trên thuộc tính và gọi điều đó - không có biểu thức sẵn có cho gán giá trị sau khi xây dựng (mặc dù là supported in 4.0).

(chỉnh sửa) như sau:

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

chỉ tìm kiếm trên một chủ đề tương tự và điều này xuất hiện đầu tiên trên google và trả lời câu hỏi của tôi: D –

+0

Hoạt động tốt - nhưng nếu hiệu suất là một vấn đề, hãy xem http://stackoverflow.com/a/14708196/188926 – Dunc

1

Bạn muốn xem xét cung cấp riêng PropertyDescriptor của bạn thông qua TypeConverter hoặc một số nguồn khác.

Tôi đã thực hiện chính xác những gì bạn mô tả cho dự án hiện tại (xin lỗi, thương mại, khác tôi sẽ chia sẻ), bằng cách lấy từ BindingSource và cung cấp thông tin qua đó.

Ý tưởng là như sau:

Tất cả bạn cần làm là, một khi bạn có các loại là tạo ra ít 'ngăn xếp' cho getter và setter tài sản, và những người bạn có thể thu thập qua đi bộ bất động sản cây thuộc loại và độ rộng thuộc tính của nó đầu tiên, giới hạn độ sâu đến một số cấp cụ thể và loại bỏ tham chiếu vòng tròn tùy thuộc vào cấu trúc dữ liệu của bạn.

Tôi đang sử dụng này khá thành công với các đối tượng Linq2SQL và kết hợp với danh sách liên kết của họ :)

-8

Biểu Tree

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

Nếu bất cứ ai quan tâm đến thương mại-off hiệu suất giữa cách tiếp cận simple reflection (cũng tốt đẹp ví dụ herehere) và cách tiếp cận Expression-building của Marc ...

Thử nghiệm của tôi liên quan đến việc sở hữu tài sản tương đối sâu (ABCDE) 10.000 lần.

  1. phản ánh đơn giản: 64 ms
  2. Biểu hiện xây dựng: 1684 ms

Rõ ràng đây là một thử nghiệm rất cụ thể, và tôi đã không được coi optimisations hoặc thiết lập các thuộc, nhưng tôi nghĩ rằng một 26x hiệu suất đạt được đáng chú ý.

+1

Có các tuyến đường khác trong kịch bản đó; một 'Func <,>' được tạo thông qua 'ILGenerator' có thể rất nhanh miễn là nó được lưu trữ và không được tạo lại cho mỗi cuộc gọi –

+1

@Marc Đồng ý, chắc chắn có khả năng bộ nhớ đệm và thử nghiệm này rất nguyên thủy ở chỗ nó chỉ mù quáng gọi phương thức BuildGet 1000 lần. Tôi đoán nó chỉ là một cảnh báo cho những người copy-pasters (như tôi!), Những người cần giải pháp OOTB nhanh nhất. – Dunc

+0

Tôi nhận được kết quả tương tự trong LinqPad - https://gist.github.com/zaus/6884806; Tôi nghĩ rằng nó có thể giúp đỡ nếu biểu thức không nhận được toàn bộ 'PropertyInfo', nhưng nó không (chỉ trông" sạch hơn ") – drzaus

Các vấn đề liên quan