Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/270.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 表达式<;Func<;t模型,字符串>&燃气轮机;表达<;行动<;TModel>&燃气轮机&引用;“吸气剂”;至;塞特;_C#_Lambda_Expression - Fatal编程技术网

C# 表达式<;Func<;t模型,字符串>&燃气轮机;表达<;行动<;TModel>&燃气轮机&引用;“吸气剂”;至;塞特;

C# 表达式<;Func<;t模型,字符串>&燃气轮机;表达<;行动<;TModel>&燃气轮机&引用;“吸气剂”;至;塞特;,c#,lambda,expression,C#,Lambda,Expression,我不熟悉表达式,我想知道如何转换我的表达式 假设在本例中,我的TModel是Customer类型,并将其分配到如下位置: Expression<Func<TModel, string>> getvalueexpression = customer =>customer.Name Expression getvalueexpression=customer=>customer.Name 差不多 Expression<Action<TModel,stri

我不熟悉表达式,我想知道如何转换我的表达式

假设在本例中,我的TModel是Customer类型,并将其分配到如下位置:

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

Expression<Action<TModel,string>> setvalueexpression = [PSEUDOCODE] getvalueexpression = input
Action<TModel,string> Setter  = setvalueexpression.Compile();
Setter(mycustomer,value);
Expression setvalueexpression=[PSEUDOCODE]getvalueexpression=input
Action Setter=setvalueexpression.Compile();
Setter(我的客户,价值);

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

我有一个helper方法,它返回属性的属性信息:

public static PropertyInfo GetPropertyInfo<T, U>(Expression<Func<T, U>> 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");
}
publicstaticpropertyinfo GetPropertyInfo(expressionproperty),其中T:class
{
var memberExpression=(property.Body作为memberExpression);
if(memberExpression!=null&&memberExpression.Member为PropertyInfo)
{
返回memberExpression.Member作为PropertyInfo;
}
抛出新的InvalidOperationException(“GetPropertyInfo的使用无效”);
}
用法:
GetPropertyInfo((MyClass c)=>c.PropertyName)

然后可以使用PropertyInfo设置类的属性值


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

修改版。这个类可能比你能找到的其他很多类要好:-)这是因为这个版本支持直接属性(
p=>p.B
)(和其他所有人一样:-)、嵌套属性(
p=>p.B.C.D
)、字段(包括“终端”和“中间”)因此在
p=>p.B.C.D
B
D
都可以是字段)和类型的“内部”强制转换(so
p=>((B类型)p.B).C.D
p=>(p.B作为B类型).C.D)
。唯一不受支持的是强制转换“terminal”元素(因此没有
p=>(object)p.B

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

部分代码不是我的。最初的(简单的)版本基于Fluent NHibernate代码(但它只支持直接属性),其他一些部分基于和的代码

公共静态类FluentTools
{
公共静态操作GetterToSetter(表达式getter)
{
参数表达式参数;
表达实例;
MemberExpression propertyOrField;
GetMemberExpression(getter、out参数、out实例、out属性或字段);
//非常简单的情况:p=>p.Property或p=>p.Field
if(参数==实例)
{
if(propertyOrField.Member.MemberType==MemberTypes.Property)
{
//这比表达式树快!(在我的基准测试中是5倍),但只适用于属性
PropertyInfo property=propertyOrField.PropertyInfo成员;
MethodInfo setter=property.GetSetMethod();
var action=(action)Delegate.CreateDelegate(typeof(action),setter);
返回动作;
}
#区域.NET 3.5
else//if(propertyOrField.Member.MemberType==MemberTypes.Field)
{
//比4.0方法慢1.2倍,比3.5方法快5倍
FieldInfo field=propertyOrField。成员为FieldInfo;
var动作=字段设置器(字段);
返回动作;
}
#端区
}
ParameterExpression值=表达式参数(typeof(TValue),“val”);
表达式expr=null;
#区域.NET 3.5
if(propertyOrField.Member.MemberType==MemberTypes.Property)
{
PropertyInfo property=propertyOrField.PropertyInfo成员;
MethodInfo setter=property.GetSetMethod();
expr=Expression.Call(实例、setter、值);
}
else//if(propertyOrField.Member.MemberType==MemberTypes.Field)
{
expr=FieldSetter(propertyOrField,value);
}
#端区
//#区域.NET 4.0
////对于字段访问,它比3.5方法快5倍,比“简单”方法快1.2倍。对于属性访问,它的速度几乎相同(快1.1倍)。
//expr=Expression.Assign(propertyOrField,value);
//#端区
返回表达式.Lambda(表达式,参数,值).Compile();
}
私有静态void GetMemberExpression(表达式表达式表达式、out参数Expression参数、out表达式实例、out MemberExpression属性或字段)
{
Expression current=Expression.Body;
while(current.NodeType==ExpressionType.Convert | | current.NodeType==ExpressionType.TypeAs)
{
当前=(当前为一元表达式)。操作数;
}
if(current.NodeType!=ExpressionType.MemberAccess)
{
抛出新ArgumentException();
}
propertyOrField=当前为MemberExpression;
当前=propertyOrField.Expression;
实例=当前;
while(current.NodeType!=ExpressionType.Parameter)
{
if(current.NodeType==ExpressionType.Convert | | current.NodeType==ExpressionType.TypeAs)
{
当前=(当前为一元表达式)。操作数;
}
else if(current.Node
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
}
static Expression<Action<T, TProperty>> MakeSetter<T, TProperty>(Expression<Func<T, TProperty>> getter)
{
    var memberExpr = (MemberExpression)getter.Body;
    var @this = Expression.Parameter(typeof(T), "$this");
    var value = Expression.Parameter(typeof(TProperty), "value");
    return Expression.Lambda<Action<T, TProperty>>(
        Expression.Assign(Expression.MakeMemberAccess(@this, memberExpr.Member), value),
        @this, value);
}
    public static Action<T, object> GenerateSetterAction<T>(PropertyInfo pi)
    {
        //p=> p.<pi>=(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<Action<T, object>>(mma, expParamP, expParamV);

        return exp.Compile();
    }
public static Action<TObject, TPropertyOnObject> GetSetter<TObject, TPropertyOnObject>(Expression<Func<TObject, TPropertyOnObject>> getterExpression)
    {
        /*** SIMPLE PROPERTIES AND FIELDS ***/
        // check if the getter expression refers 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<object, object> 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<TObject, TPropertyOnObject> 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", <value to be set>) 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 "dictionary getter" expression is for and a value
                // to be added to the dictionary.
                Action<TObject, TPropertyOnObject> 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<TObject, TPropertyOnObject> collectionInserter = (expressionParameter, value) =>
                {
                    try
                    {
                        var collectionInstance = (IList<TPropertyOnObject>)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);
    }