表达式来; Func键< TModel的,串>>表达<动作< TModel>> "吸气剂"到“Setter” [英] Expression<Func<TModel,string>> to Expression<Action<TModel>> "Getter" to "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键< TModel的,串>>表达<动作< TModel>> "吸气剂"到“Setter”的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!