Извлечь делегата (действие <TObject, TValue>) для установщика индексатора с помощью ExpressionTree - PullRequest
2 голосов
/ 08 июля 2011

Я пытаюсь создать делегатов для доступа к любым методам получения и установки свойств.

Я нашел следующий код (в этом посте: driis о глубоких свойствах ), который работает очень хорошо:

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

Например: когда я создаю установщик из параметра «Выражение в», я получаю своих делегатов получателя и установщика, например:

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

Моя проблема с такими индексаторами:

public object this[int id]

Когда я вызываю подчиненное свойство индексатора, проблем не возникает. Он работает нормально, так как доступ к индексатору с внутренним именем «Item» можно получить, прочитав его:

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

выражение:

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

Но я не могу найти способ получить внутреннее сеттерное выражение. Моя цель - получить что-то вроде этого:

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

Но я не могу использовать назначение с лямбдой, как этот:

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

Я получаю сообщение об ошибке CS0832: Дерево выражений может не содержать оператор присваивания.

Есть ли способ написать лямбда-выражение, чтобы найти тело "set_Item (2, value)" в параметре Expression> ???

Заранее спасибо, я знаю, что это сложный вопрос ...

1 Ответ

1 голос
/ 09 июля 2011

Если я вас правильно понимаю, вам нужен метод, который будет работать точно так же, как тот, который у вас уже есть, но он также работает для чего-то вроде

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

Если это так, должно работать что-то вроде этого:

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

Первая часть - это в основном копия вашего метода.Вторая часть проверяет, является ли выражение вызовом метода получателя и, если он есть, заменяет его вызовом метода установщика.Существует некоторая работа с параметрами, но кроме этого, она довольно прямолинейна.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...