我们在这个例子中说我的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 //根据stackoverflow/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>)); } //根据stackoverflow/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.Nameto 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 stackoverflow/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 stackoverflow/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键< TModel的,串>>表达<动作< TModel>
发布评论