表达式来; Func键< TModel的,串>>表达<动作< TModel>> "吸气剂"到“Setter” [英] Expression<Func<TModel,string>> to Expression<Action<TModel>> "Getter" to "Setter"

查看:111
本文介绍了表达式来; Func键< TModel的,串>>表达<动作< TModel>> "吸气剂"到“Setter”的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很喜欢表达,而且我想知道如何以任何方式转换我的表达式



我们在这个例子中说我的TModel是Customer类型,并分配给这样的位置:

 表达式< Func< TModel,string>> getvalueexpression = customer => customer.Name 

 表达式< Action< TModel,string>> setvalueexpression = [PSEUDOCODE] getvalueexpression = input 
Action< TModel,string> Setter = setvalueexpression.Compile();
Setter(mycustomer,value);

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

解决方案

修改版本。这个类可能比许多其他你可以找到的更好:-)这是因为这个版本支持直接属性( p => pB )(和其他人一样 - )),嵌套属性( p => pBCD ),字段(两个终端和中间,所以在 p => ; pBCD B D 可以是字段)和类型的内部 (所以 p =>((BType)pB).CD p =>(pB为BType).CD)。唯一不支持的是终端元素的转换(所以没有 p =>(object)p.B )。



生成器中有两个codepath:对于简单表达式( p => pB )和嵌套表达式。有.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>(表达式< Func< T,TValue> getter)
{
ParameterExpression参数;
表达式实例;
MemberExpression propertyOrField;

GetMemberExpression(getter,out参数,out instance,out propertyOrField);

//很简单的情况:p => p.Property或p => p.Field
if(parameter == instance)
{
if(propertyOrField.MemberType == MemberTypes.Property)
{
//这是更快比表达树! (在我的基准测试上为5x),但仅适用于属性
PropertyInfo property = propertyOrField.Member as PropertyInfo;
MethodInfo setter = property.GetSetMethod();
var action =(Action< T,TValue>)Delegate.CreateDelegate(typeof(Action< T,TValue>),setter);
返回动作;
}
#region .NET 3.5
else // if(propertyOrField.MemberType == MemberTypes.Field)
{
// 1.2倍慢于4.0方法,比3.5方法快5倍
FieldInfo field = propertyOrField.Member as FieldInfo;
var action = FieldSetter< T,TValue>(field);
返回动作;
}
#endregion
}

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

表达式expr = null;

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

//#区域.NET 4.0
////对于现场访问,它比3.5方法快5倍,比简单 方法。对于物业访问速度几乎相同(1.1倍)。
// expr = Expression.Assign(propertyOrField,value);
//#endregion

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

private static void GetMemberExpression< T,U>(表达式< Func< T,U>>表达式,out ParameterExpression参数,out表达式实例,out MemberExpression propertyOrField)
{
表达式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 =当前为MemberExpression;
current = propertyOrField.Expression;

instance = current;

while(current.NodeType!= ExpressionType.Parameter)
{
if(current.NodeType == ExpressionType.Convert || current.NodeType == ExpressionType.TypeAs)
{
current =(当前为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

//根据https://stackoverflow.com/questions/321650/how-do-i-set- a-field-value-in-a-c-expression-tree / 321686#321686
private static Action< T,TValue> FieldSetter< T,TValue>(FieldInfo字段)
{
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>));
}

//根据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)//注意'ref',这是
{//分配$ b时很重要$ b left = right; //到值类型!
}

#endregion
}


I'm new to expressions, and i'd like to know how if it's in any way possible to convert my expression

Let's say in this example my TModel is of type Customer, and assigned it somewhere like this:

Expression<Func<TModel, string>> getvalueexpression = customer =>customer.Name

to something like

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

So in short, i want to somehow build and compile an expression that sets the customer name specified by my getter expression, to a specific value.

解决方案

Modified version. This class is probably better than many other ones you can find around :-) This is because this version support direct properties (p => p.B) (as everyone else :-) ), nested properties (p => p.B.C.D), fields (both "terminal" and "in the middle", so in p => p.B.C.D both B and D could be fields) and "inner" casting of types (so p => ((BType)p.B).C.D and p => (p.B as BType).C.D). The only thing that isn't supported is casting of the "terminal" element (so no p => (object)p.B).

There are two "codepaths" in the generator: for simple Expressions (p => p.B) and for "nested" expressions. There are code variants for .NET 4.0 (that has the Expression.Assign expression type). From some benchmarks of mine the fastest delegates are: "simple" Delegate.CreateDelegate for properties, Expression.Assign for fields and "simple" FieldSetter for fields (this one is just a little slower than Expression.Assign for fields). So under .NET 4.0 you should take away all the code marked as 3.5.

Part of the code isn't mine. The initial (simple) version was based on the Fluent NHibernate code (but it supported only direct properties), some other parts are based on code from How do I set a field value in an C# Expression tree? and Assignment in .NET 3.5 expression trees.

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
}

这篇关于表达式来; Func键&LT; TModel的,串&GT;&GT;表达&lt;动作&lt; TModel&gt;&gt; &QUOT;吸气剂&QUOT;到“Setter”的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆