11

给定一个字符串:“Person.Address.Postcode”我希望能够在 Person 的实例上获取/设置这个 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; }
}
4

5 回答 5

21

听起来你是用正则反射排序的,但对于信息,为嵌套属性构建表达式的代码与这个 order-by code非常相似。

请注意,要设置一个值,您需要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();
    }
}
于 2009-02-11T15:01:33.957 回答
2

为什么不使用递归?就像是:

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
}
于 2009-02-11T14:16:35.313 回答
2

如果有人对简单反射方法(这里这里也有很好的例子)和 Marc 的表达式构建方法之间的性能权衡感兴趣......

我的测试涉及获得相对深度的属性 (ABCDE) 10,000 次。

  1. 简单反射:64 ms
  2. 表达构建:1684 毫秒

显然,这是一个非常具体的测试,我没有考虑优化或设置属性,但我认为 26 倍的性能命中是值得注意的。

于 2013-02-05T13:02:14.493 回答
1

您想查看通过 TypeConverter 或其他来源提供您自己的 PropertyDescriptor。

通过从 BindingSource 派生并通过那里提供信息,我已经完全实现了您对当前项目的描述(对不起,商业,否则我会分享)。

思路如下:

所有你需要做的是,一旦你有了类型,就是为属性的 getter 和 setter 创建小的“堆栈”,你可以通过首先遍历类型的属性树及其属性广度来收集这些堆栈,将深度限制为指定数量的级别并根据您的数据结构删除循环引用。

我在 Linq2SQL 对象中非常成功地使用了它,并结合了它们的绑定列表:)

于 2009-03-17T21:10:37.290 回答
-10

表达式树

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]);
}
于 2009-07-06T07:40:15.163 回答