كيفية إنشاء شجرة التعبير / Lambda لممتلكات عميقة من سلسلة

StackOverflow https://stackoverflow.com/questions/536932

  •  22-08-2019
  •  | 
  •  

سؤال

نظرا لسلسلة: "شخص .Address.postcode" أريد أن أكون قادرا على الحصول على / ضبط خاصية الرمز البريدي هذه على مثيل شخص. كيف يمكنني أن أفعل هذا؟ كانت فكرتي تقسيم السلسلة عن طريق "." ثم تكرر أكثر من الأجزاء، تبحث عن الممتلكات في النوع السابق، ثم قم ببناء شجرة تعبيرية تبدو مثل (الاعتذار عن بناء جملة الزائفة):

(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; }
}
هل كانت مفيدة؟

المحلول

لماذا لا تستخدم العودية؟ شيء مثل:

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
}

نصائح أخرى

يبدو أنك تم تصنيفك مع انعكاس منتظم، ولكن للحصول على معلومات، فإن التعليمات البرمجية لبناء تعبير عن خصائص متداخلة سيكون مشابهة جدا هذا الطلب رمز.

لاحظ أنه لتعيين قيمة، تحتاج إلى استخدام 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();
    }
}

إذا كان أي شخص مهتما بموقف الأداء بين انعكاس بسيط النهج (أمثلة لطيفة أيضا هنا و هنا) ومارك مبنى التعبير نهج...

يشارك اختباري في الحصول على خاصية عميقة نسبيا (ABCDE) 10000 مرة.

  1. انعكاس بسيط: 64 مللي ثانية
  2. مبنى التعبير: 1684 مللي ثانية

من الواضح أن هذا اختبار محدد للغاية، وأنا لم أقم بتعيين تحسينات أو إعداد الخصائص، لكنني أعتقد أن أداء 26x يضرب يستحق الإشارة.

تريد أن ننظر إلى توفير محاكاة الخصائص الخاصة بك عبر Typeconverter أو بعض المصدر الآخر.

لقد قمت بالتنفيذ بالضبط ما تصفه للمشروع الحالي (آسف، تجاري، وأود أن أشاركه)، من خلال الاستمتاع من BindingSource، وتوفير المعلومات عبر هناك.

الفكرة كالتالي:

كل ما عليك فعله هو، بمجرد أن يكون لديك النوع هو إنشاء "مكدسات" صغيرة للحصول على خصائص Getter و Setters من الخصائص، وأولئك الذين يمكنك جمعهم عبر المشي شجرة العقار من النوع وخصائصها الأولى، مما يحد من أعماق عدد محدد من المستويات وإزالة المراجع الدائرية اعتمادا على هياكل البيانات الخاصة بك.

أنا أستخدم هذا بنجاح تماما مع كائنات LinQ2SQL والتركيبة مع قوائم ربطها :)

شجرة التعبير

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]);
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top