表达式<Func >到Expression <Action >“Getter”到“Setter”

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

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

Expression<Func> getvalueexpression = customer =>customer.Name 

喜欢的东西

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

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

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

生成器中有两个“代码路径”:简单表达式( 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 GetterToSetter(Expression> 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)Delegate.CreateDelegate(typeof(Action), 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(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>(expr, parameter, value).Compile(); } private static void GetMemberExpression(Expression> 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 FieldSetter(FieldInfo field) { DynamicMethod m = new DynamicMethod("setter", typeof(void), new Type[] { typeof(T), typeof(TValue) }, typeof(FluentTools)); ILGenerator cg = m.GetILGenerator(); // arg0. = arg1 cg.Emit(OpCodes.Ldarg_0); cg.Emit(OpCodes.Ldarg_1); cg.Emit(OpCodes.Stfld, field); cg.Emit(OpCodes.Ret); return (Action)m.CreateDelegate(typeof(Action)); } // 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(ref T left, T right) // note the 'ref', which is { // important when assigning left = right; // to value types! } #endregion } 
 static Expression> MakeSetter(Expression> getter) { var memberExpr = (MemberExpression)getter.Body; var @this = Expression.Parameter(typeof(T), "$this"); var value = Expression.Parameter(typeof(TProperty), "value"); return Expression.Lambda>( Expression.Assign(Expression.MakeMemberAccess(@this, memberExpr.Member), value), @this, value); } 

我有这个帮助方法,它返回属性的属性信息:

 public static PropertyInfo GetPropertyInfo(Expression> property) where T : class { var memberExpression = (property.Body as MemberExpression); if (memberExpression != null && memberExpression.Member is PropertyInfo) { return memberExpression.Member as PropertyInfo; } throw new InvalidOperationException("Invalid usage of GetPropertyInfo"); } 

用法: GetPropertyInfo((MyClass c) => c.PropertyName);

然后,您可以使用PropertyInfo在类上设置属性的值。

您需要修改代码以满足您的需求,但希望它会有所帮助。

这是我的方式

  public static Action GenerateSetterAction(PropertyInfo pi) { //p=> p.=(pi.PropertyType)v var expParamP = Expression.Parameter(typeof(T), "p"); var expParamV = Expression.Parameter(typeof(object), "v"); var expParamVc = Expression.Convert(expParamV, pi.PropertyType); var mma = Expression.Call( expParamP , pi.GetSetMethod() , expParamVc ); var exp = Expression.Lambda>(mma, expParamP, expParamV); return exp.Compile(); } 

由于正确的答案对我不起作用(表达式中的集合)但是把我推向了正确的方向,我需要对此进行大量调查,我想我想出了一种方法,可以为字面上的任何成员表达式生成setter。

对于属性和字段,它的行为与标记的答案相同(我相信它更透明)。

它还有对列表和词典的额外支持 – 请参阅注释。

 public static Action GetSetter(Expression> getterExpression) { /*** SIMPLE PROPERTIES AND FIELDS ***/ // check if the getter expression reffers directly to a PROPERTY or FIELD var memberAcessExpression = getterExpression.Body as MemberExpression; if (memberAcessExpression != null) { //to here we assign the SetValue method of a property or field Action propertyOrFieldSetValue = null; // property var propertyInfo = memberAcessExpression.Member as PropertyInfo; if (propertyInfo != null) { propertyOrFieldSetValue = (declaringObjectInstance, propertyOrFieldValue) => propertyInfo.SetValue(declaringObjectInstance, propertyOrFieldValue); }; // field var fieldInfo = memberAcessExpression.Member as FieldInfo; if (fieldInfo != null) { propertyOrFieldSetValue = (declaringObjectInstance, propertyOrFieldValue) => fieldInfo.SetValue(declaringObjectInstance, propertyOrFieldValue); } // This is the expression to get declaring object instance. // Example: for expression "o=>o.Property1.Property2.CollectionProperty[3].TargetProperty" it gives us the "o.Property1.Property2.CollectionProperty[3]" part var memberAcessExpressionCompiledLambda = Expression.Lambda(memberAcessExpression.Expression, getterExpression.Parameters.Single()).Compile(); Action setter = (expressionParameter, value) => { // get the object instance on which is the property we want to set var declaringObjectInstance = memberAcessExpressionCompiledLambda.DynamicInvoke(expressionParameter); Debug.Assert(propertyOrFieldSetValue != null, "propertyOrFieldSetValue != null"); // set the value of the property propertyOrFieldSetValue(declaringObjectInstance, value); }; return setter; } /*** COLLECTIONS ( IDictionary<,> and IList<,>) ***/ /* * DICTIONARY: * Sample expression: * "myObj => myObj.Property1.ListProperty[5].AdditionalInfo["KEY"]" * Setter behaviour: * The same as adding to a dictionary. * It does Add("KEY", ) to the dictionary. It fails if the jey already exists. * * * LIST * Sample expression: * "myObj => myObj.Property1.ListProperty[INDEX]" * Setter behaviour: * If INDEX >= 0 and the index exists in the collection it behaves the same like inserting to a collection. * IF INDEX < 0 (is negative) it adds the value at the end of the collection. */ var methodCallExpression = getterExpression.Body as MethodCallExpression; if ( methodCallExpression != null && methodCallExpression.Object != null && methodCallExpression.Object.Type.IsGenericType) { var collectionGetterExpression = methodCallExpression.Object as MemberExpression; Debug.Assert(collectionGetterExpression != null, "collectionGetterExpression != null"); // This gives us the collection instance when it is invoked on the object instance whic the expression is for var collectionGetterCompiledLambda =Expression.Lambda(collectionGetterExpression, getterExpression.Parameters.Single()).Compile(); // this returns the "KEY" which is the key (object) in case of Dictionaries and Index (integer) in case of other collections var collectionKey = ((ConstantExpression) methodCallExpression.Arguments[0]).Value; var collectionType = collectionGetterExpression.Type; // IDICTIONARY if (collectionType.GetGenericTypeDefinition() == typeof(IDictionary<,>)) { // Create an action which accepts the instance of the object which the "dictionarry getter" expression is for and a value // to be added to the dictionary. Action dictionaryAdder = (expressionParameter, value) => { try { var dictionaryInstance = (IDictionary)collectionGetterCompiledLambda.DynamicInvoke(expressionParameter); dictionaryInstance.Add(collectionKey, value); } catch (Exception exception) { throw new Exception( string.Format( "Addition to dictionary failed [Key='{0}', Value='{1}']. The \"adder\" was generated from getter expression: '{2}'.", collectionKey, value, getterExpression.ToString()), exception); } }; return dictionaryAdder; } // ILIST if (typeof (IList<>).MakeGenericType(typeof (bool)).IsAssignableFrom(collectionType.GetGenericTypeDefinition().MakeGenericType(typeof(bool)))) { // Create an action which accepts the instance of the object which the "collection getter" expression is for and a value // to be inserted Action collectionInserter = (expressionParameter, value) => { try { var collectionInstance = (IList)collectionGetterCompiledLambda.DynamicInvoke(expressionParameter); var collectionIndexFromExpression = int.Parse(collectionKey.ToString()); // The semantics of a collection setter is to add value if the index in expression is <0 and set the item at the index // if the index >=0. if (collectionIndexFromExpression < 0) { collectionInstance.Add(value); } else { collectionInstance[collectionIndexFromExpression] = value; } } catch (Exception invocationException) { throw new Exception( string.Format( "Insertion to collection failed [Index='{0}', Value='{1}']. The \"inserter\" was generated from getter expression: '{2}'.", collectionKey, value, getterExpression.ToString()), invocationException); } }; return collectionInserter; } throw new NotSupportedException( string.Format( "Cannot generate setter from the given expression: '{0}'. Collection type: '{1}' not supported.", getterExpression, collectionType)); } throw new NotSupportedException("Cannot generate setter from the given expression: "+getterExpression); }