表达式<Func <TModel,string >>到Expression <Action <TModel >>"Getter"到"Setter"

Mic*_*lle 9 c# lambda expression

我是表达式的新手,我想知道如何以任何方式转换我的表达式

让我们说在这个例子中我的TModel是Customer类型,并将它分配给这样的地方:

Expression<Func<TModel, string>> getvalueexpression = customer =>customer.Name
Run Code Online (Sandbox Code Playgroud)

喜欢的东西

Expression<Action<TModel,string>> setvalueexpression = [PSEUDOCODE] getvalueexpression = input
Action<TModel,string> Setter  = setvalueexpression.Compile();
Setter(mycustomer,value);
Run Code Online (Sandbox Code Playgroud)

所以简而言之,我想以某种方式构建和编译一个表达式,该表达式将我的getter表达式指定的客户名称设置为特定值.

xan*_*tos 11

修改版.这个类可能比你可以找到的许多其他类更好:-)这是因为这个版本支持直接属性(p => p.B)(和其他人一样:-)),嵌套属性(p => p.B.C.D),字段(都是"终端"和"中间",所以在p => p.B.C.D这两个BD可能是字段)和'’(内的类型,以便铸造p => ((BType)p.B).C.Dp => (p.B as BType).C.D)(所以没有终端'元素.不支持的唯一事情是铸造的’ p => (object)p.B).

生成器中有两个"代码路径":简单的表达式(p => p.B)和"嵌套"表达式..NET 4.0有代码变体(具有Expression.Assign表达式类型).从我的一些基准测试来看,最快的代表是:"简单" Delegate.CreateDelegate属性,Expression.Assign字段和"简单" FieldSetter字段(这个只比Expression.Assign字段慢一点).因此,在.NET 4.0下,您应该删除标记为3.5的所有代码.

部分代码不是我的.初始(简单)版本基于Fluent NHibernate代码(但它仅支持直接属性),其他一些部分基于如何在C#表达式树中设置字段值的代码.NET 3.5表达式树中的赋值.

public static class FluentTools
{
    public static Action<T, TValue> GetterToSetter<T, TValue>(Expression<Func<T, TValue>> getter)
    {
        ParameterExpression parameter;
        Expression instance;
        MemberExpression propertyOrField;

        GetMemberExpression(getter, out parameter, out instance, out propertyOrField);

        // Very simple case: p => p.Property or p => p.Field
        if (parameter == instance)
        {
            if (propertyOrField.Member.MemberType == MemberTypes.Property)
            {
                // This is FASTER than Expression trees! (5x on my benchmarks) but works only on properties
                PropertyInfo property = propertyOrField.Member as PropertyInfo;
                MethodInfo setter = property.GetSetMethod();
                var action = (Action<T, TValue>)Delegate.CreateDelegate(typeof(Action<T, TValue>), setter);
                return action;
            }
            #region .NET 3.5
            else // if (propertyOrField.Member.MemberType == MemberTypes.Field)
            {
                // 1.2x slower than 4.0 method, 5x faster than 3.5 method
                FieldInfo field = propertyOrField.Member as FieldInfo;
                var action = FieldSetter<T, TValue>(field);
                return action;
            }
            #endregion
        }

        ParameterExpression value = Expression.Parameter(typeof(TValue), "val");

        Expression expr = null;

        #region .NET 3.5
        if (propertyOrField.Member.MemberType == MemberTypes.Property)
        {
            PropertyInfo property = propertyOrField.Member as PropertyInfo;
            MethodInfo setter = property.GetSetMethod();
            expr = Expression.Call(instance, setter, value);
        }
        else // if (propertyOrField.Member.MemberType == MemberTypes.Field)
        {
            expr = FieldSetter(propertyOrField, value);
        }
        #endregion

        //#region .NET 4.0
        //// For field access it's 5x faster than the 3.5 method and 1.2x than "simple" method. For property access nearly same speed (1.1x faster).
        //expr = Expression.Assign(propertyOrField, value);
        //#endregion

        return Expression.Lambda<Action<T, TValue>>(expr, parameter, value).Compile();
    }

    private static void GetMemberExpression<T, U>(Expression<Func<T, U>> expression, out ParameterExpression parameter, out Expression instance, out MemberExpression propertyOrField)
    {
        Expression current = expression.Body;

        while (current.NodeType == ExpressionType.Convert || current.NodeType == ExpressionType.TypeAs)
        {
            current = (current as UnaryExpression).Operand;
        }

        if (current.NodeType != ExpressionType.MemberAccess)
        {
            throw new ArgumentException();
        }

        propertyOrField = current as MemberExpression;
        current = propertyOrField.Expression;

        instance = current;

        while (current.NodeType != ExpressionType.Parameter)
        {
            if (current.NodeType == ExpressionType.Convert || current.NodeType == ExpressionType.TypeAs)
            {
                current = (current as UnaryExpression).Operand;
            }
            else if (current.NodeType == ExpressionType.MemberAccess)
            {
                current = (current as MemberExpression).Expression;
            }
            else
            {
                throw new ArgumentException();
            }
        }

        parameter = current as ParameterExpression;
    }

    #region .NET 3.5

    // Based on https://stackoverflow.com/questions/321650/how-do-i-set-a-field-value-in-an-c-expression-tree/321686#321686
    private static Action<T, TValue> FieldSetter<T, TValue>(FieldInfo field)
    {
        DynamicMethod m = new DynamicMethod("setter", typeof(void), new Type[] { typeof(T), typeof(TValue) }, typeof(FluentTools));
        ILGenerator cg = m.GetILGenerator();

        // arg0.<field> = arg1
        cg.Emit(OpCodes.Ldarg_0);
        cg.Emit(OpCodes.Ldarg_1);
        cg.Emit(OpCodes.Stfld, field);
        cg.Emit(OpCodes.Ret);

        return (Action<T, TValue>)m.CreateDelegate(typeof(Action<T, TValue>));
    }

    // Based on https://stackoverflow.com/questions/208969/assignment-in-net-3-5-expression-trees/3972359#3972359
    private static Expression FieldSetter(Expression left, Expression right)
    {
        return
            Expression.Call(
                null,
                typeof(FluentTools)
                    .GetMethod("AssignTo", BindingFlags.NonPublic | BindingFlags.Static)
                    .MakeGenericMethod(left.Type),
                left,
                right);
    }

    private static void AssignTo<T>(ref T left, T right)  // note the 'ref', which is
    {                                                     // important when assigning
        left = right;                                     // to value types!
    }

    #endregion
}
Run Code Online (Sandbox Code Playgroud)