简体   繁体   English

检索委托(操作<tobject, tvalue> ) 到带有 ExpressionTree 的索引器的设置器</tobject,>

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

I'm trying to create delegates to access any Property Get & Set methods.我正在尝试创建代表来访问任何 Property Get & Set 方法。

I have found the following code (on this post: driis about deep properties ) which works really fine:我找到了以下代码(在这篇文章中: driis about deep properties ),它工作得很好:

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

For example: when I create the setter from the Expression in parameter, I obtain my getter and setter delegates like these ones:例如:当我从 Expression in 参数创建 setter 时,我获得了我的 getter 和 setter 委托,如下所示:

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:它工作正常,因为通过读取它来访问内部命名为“Item”的索引器:

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:但我不能像这样使用 Lambda 的赋值:

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

I get an error CS0832: An expression tree may not contain an assignment operator.我收到错误 CS0832:表达式树可能不包含赋值运算符。

Is there a way to write a Lambda Expression in order to find the "set_Item(2, value)" body in a Expression> parameter???有没有办法编写 Lambda 表达式以便在 Expression> 参数中找到“set_Item(2, value)”主体?

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.第二部分检查表达式是否是对 getter 方法的调用,如果是,则将其替换为对 setter 方法的调用。 There is some work regarding parameters, but other than that, quite straight-forward.有一些关于参数的工作,但除此之外,非常简单。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM