开发者

Retrieve a delegate (Action<TObject, TValue>) to the Setter of an Indexer with ExpressionTree

开发者 https://www.devze.com 2023-03-18 03:32 出处:网络
I\'m trying to create delegates to access any Property Get & Set methods. I have found the following code (on this post : driis about deep properties ) which works really fine :

I'm trying to create delegates to access any Property Get & Set methods.

I have found the following code (on this post : driis about deep properties ) which works really fine :

public static class Extractor<TObject> where TObject : class
{
    public static DelegateAccessor<TObject, TValue> GetAccessorsDelegates<TValue>(Expression<Func<TObject, TValue>> expression)
    {
        Func<TObject, TValue> getter = expression.Compile();

        ParameterExpression pObj = expression.Parameters[0];
        ParameterExpression pValue = Expression.Parameter(typeof(TValue), "value");
        BlockExpression setterBlock = Expression.Block(Expression.Assign(expression.Body, pValue));
        Expression<Action<TObject, TValue>> setterExpression = Expression.Lambda<Action<TObject, TValue>>(setterBlock, pObj, pValue);
        Action<TObject, TValue> setter = setterExpression.Compile();

        return new DelegateAccessor<TObject, TValue>(getter,setter);
    }
}

开发者_JAVA技巧For example : when I create the setter from the Expression in parameter, I obtain my getter and setter delegates like these ones :

get : (t => t.ID)
set : (t, value) => { t.ID = value}

My problem is with indexers like this one :

public object this[int id]

When I call with a sub property of an indexer, there is no problem. It works fine since the indexer, internally named "Item" is accessed by reading it :

Extractor<MyObject>.DelegateAccessor(t => t.MySubTable[2].MyProperty)

the expression is :

t => t.MySubTable.get_Item(2).MyProperty

But I don't find a way to get the internal setter expression. My goal is to obtain something like this :

(t, value) => { t.MySubTable.set_Item(2, value) }

But I can't use assignment with a Lambda like this one :

(t,v) => t.SubStrings[0] = v

I get an error CS0832 : An expression tree may not contain an assignment operator.

Is there a way to write a Lambda Expression in order to find the "set_Item(2, value)" body in a Expression> parameter ???

Thanks in advance, I know it's a hard question...


If I understand you correctly, you want a method that works exactly as the one you already have, but that also works for something like

Extractor<MyObject>.DelegateAccessor(t => t.MySubTable[2])

If that's the case, something like this should work:

public static DelegateAccessor<TObject, TValue> GetAccessorsDelegates<TValue>(
    Expression<Func<TObject, TValue>> expression)
{
    Func<TObject, TValue> getter = expression.Compile();

    Action<TObject, TValue> setter = null;
    ParameterExpression pValue = Expression.Parameter(typeof(TValue), "value");
    ParameterExpression pObj = expression.Parameters[0];
    if (expression.Body is MemberExpression)
    {
        Expression setterBlock = Expression.Assign(expression.Body, pValue);
        Expression<Action<TObject, TValue>> setterExpression =
            Expression.Lambda<Action<TObject, TValue>>(setterBlock, pObj, pValue);
        setter = setterExpression.Compile();
    }
    else
    {
        var getterCall = expression.Body as MethodCallExpression;
        if (getterCall != null)
        {
            var method = getterCall.Method;
            if (method.IsSpecialName && method.Name.StartsWith("get_"))
            {
                var parameters = method.GetParameters()
                                       .Select(p => p.ParameterType)
                                       .Concat(new[] { method.ReturnType })
                                       .ToArray();
                var setterName = "set_" + method.Name.Substring(4);
                var setterMethod =
                    method.DeclaringType.GetMethod(setterName, parameters);
                var setterCall = Expression.Call(
                    getterCall.Object, setterMethod,
                    getterCall.Arguments.Concat(new[] { pValue }));
                var setterExpression =
                    Expression.Lambda<Action<TObject, TValue>>(
                        setterCall, pObj, pValue);
                setter = setterExpression.Compile();
            }
        }
    }

    return new DelegateAccessor<TObject, TValue>(getter, setter);
}

The first part is basically copy of the method you have. The second part checks whether the expression is a call to a getter method and if it is, replaces it with call to setter method. There is some work regarding parameters, but other than that, quite straight-forward.

0

精彩评论

暂无评论...
验证码 换一张
取 消