表达<FUNC <tModel的,字符串>&GT;到表达式<action <tmodel>&gt; “Getter”到“Setter”</action <tmodel> </func <tmodel,string>

时间:2011-10-11 09:10:54

标签: c# lambda expression

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

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

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

类似

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

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

5 个答案:

答案 0 :(得分:11)

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

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

部分代码不是我的。初始(简单)版本基于Fluent NHibernate代码(但它仅支持直接属性),其他一些部分基于How do I set a field value in an C# Expression tree?Assignment in .NET 3.5 expression trees的代码。

Expression.Assign

答案 1 :(得分:2)

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);
}

答案 2 :(得分:1)

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

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");
}

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

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

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

答案 3 :(得分:0)

这是我的方式

    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();
    }

答案 4 :(得分:0)

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

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

它还有对列表和词典的额外支持 - 请参阅评论。

public static Action<TObject, TPropertyOnObject> GetSetter<TObject, TPropertyOnObject>(Expression<Func<TObject, TPropertyOnObject>> 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<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 "dictionarry 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);
    }