3

我正在做的事情有点麻烦。我最初创建了一个通用层,它位于我的业务对象和工作正常的数据访问层之间。然后,我最近读到了一种叫做 Expression Trees 的东西,它显然更有效,并且已经证明是这样的,因为我用表达式交换了 Activator.CreateInstance() 并以指数方式改进了我的通用层。

我仍在阅读有关整个区域(表达式)的内容,但遇到了一些我想尝试使其通用的代码。目前,您必须传入一个具体类型,例如字符串、整数、十进制等。我是这个位是通用的。我尝试了几件事但失败了。我想要通用的一点是动作,我不想传入一个字符串,我希望能够通用地传入属性的类型,即typeof(T).GetProperty("Forename").PropertyType。这可能吗?正在考虑做一个有点 foo bar 的 switch 语句。

在此先感谢,奥南。

public class TTT<T> where T : new()
{
    public void Do(object t)
    {
        MethodInfo info = typeof(T).GetProperty("Forename").GetSetMethod();

        ParameterExpression param = Expression.Parameter(typeof(string), "val");

        MethodCallExpression call = Expression.Call(Expression.Constant(t), info,
            new ParameterExpression[] { param });

        Action<string> action = Expression.Lambda<Action<string>>(call, param).Compile();

        action("hi");
    }
}
4

1 回答 1

5

首先,请注意,这不是一个好方法;如果您正在构建Expression每个调用,然后Compile()-ing 它,然后调用它,则没有性能优势。反射会更快。如果您需要性能,请查看诸如"FastMember" 之类的库,这将是:

var accessor = TypeAccessor.Create(typeof(T));
accessor[target, "Forename"] = value;

(通过元编程和自动缓存完全优化)


如果您希望类型是动​​态的,那么有两个选项:

  • Expression.GetActionType使用和使用键入它DynamicInvoke-性能非常差(提示:不要这样做)
  • 键入它Action<object>并在表达式中进行强制转换(很好)

所以像:

using System;
using System.Linq.Expressions;
using System.Reflection;
class Foo
{
    public string Forename {get;set;}
}
class Test<T>
{
    public void Do(object target, object value)
    {
        var obj = Expression.Constant(target, typeof(T));
        var member = Expression.PropertyOrField(obj, "Forename");
        var param = Expression.Parameter(typeof(object));
        Type type;
        switch(member.Member.MemberType)
        {
            case MemberTypes.Field:
                type = ((FieldInfo)member.Member).FieldType; break;
            case MemberTypes.Property:
                type = ((PropertyInfo)member.Member).PropertyType; break;
            default:
                throw new NotSupportedException(member.Member.MemberType.ToString());
        }
        var body = Expression.Assign(member, Expression.Convert(param, type));
        var lambda = Expression.Lambda<Action<object>>(body, param);
        lambda.Compile()(value);
    }
}
static class Program
{
    static void Main()
    {
        var obj = new Foo();
        new Test<Foo>().Do(obj, "abc");
        Console.WriteLine(obj.Forename);
    }
}
于 2012-11-16T10:03:11.590 回答