【问题标题】:Expression Tree using Action<T> possible?可以使用 Action<T> 的表达式树吗?
【发布时间】:2012-11-16 09:49:55
【问题描述】:

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

我仍在阅读有关整个区域(表达式)的一些内容,但我遇到了一些我想尝试使其通用的代码。目前,您必须传入一个具体的类型,例如字符串、整数、十进制等。我是这个位是通用的。我尝试了几件事但失败了。我想要通用的位是Action,我不想传入一个字符串,我希望能够通用地传入属性的类型,即 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");
    }
}

【问题讨论】:

    标签: c# generics reflection expression-trees


    【解决方案1】:

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

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

    (通过元编程和自动缓存进行了全面优化)


    如果你希望类型是动​​态的,那么有两种选择:

    • 使用Expression.GetActionType 输入它并使用DynamicInvoke - 真的性能不佳(提示:不要这样做)
    • 将其输入为Action&lt;object&gt; 并在表达式中进行强制转换(很好)

    比如:

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

    【讨论】:

    • 您使用 propertyinfo 设置值比我建议的要快吗?我一定会查找 FastMember 方法。我想尝试仅出于学习目的实现我自己的。
    • @Oram 编译后的表达式将比反射更快如果它被缓存和重用。如果你每次都这样做,它并不比反射快。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-21
    • 2010-09-29
    • 1970-01-01
    相关资源
    最近更新 更多