Как получить экземпляр упомянутого экземпляра из лямбда-выражения - PullRequest
11 голосов
/ 31 августа 2010

У меня есть это лямбда-выражение Expression<Func<bool>> commandToExecute

Затем я передаю экземпляр класса с помощью метода:

_commandExecuter.ProcessCommand (() => aClass.Method())

Как получить экземпляр aClass в методе ProcessCommand?

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

Возможно ли это?

EDIT: Теперь я написал простой статический вспомогательный метод для получения экземпляра:

private static object GetReferredProviderInstance(Expression body)
{
    var methodCallExpression = body as MethodCallExpression;
    if (methodCallExpression != null)
    {
        var constantExpression = methodCallExpression.Object as ConstantExpression;
        if (constantExpression != null) return constantExpression.Value;
    }
    return null;
}

Вызов метода выглядит следующим образом ...

Expression body = commandToExecute.Body; // this is the method parameter Expression<Func<bool>> commandToExecute
var referredProviderInstance = GetReferredProviderInstance(body);

Проблема в том, что приведение к ConstantExpression приводит к Null. Таким образом, constantExpression всегда равно нулю.

Есть идеи?

РЕДАКТИРОВАТЬ 2 Я исправил проблему ...

private static object GetReferredProviderInstance(Expression body)
{
    var methodCallExpression = body as MethodCallExpression;
    if (methodCallExpression != null)
    {
        var memberExpression = methodCallExpression.Object as MemberExpression;
        if (memberExpression != null)
        {
            var constantExpression = memberExpression.Expression as ConstantExpression;
            if (constantExpression != null) return constantExpression.Value;
        }
    }
    return null;
}

Но тут возникает новая проблема. Я получаю только тот экземпляр формы Windows, в котором находится ссылочный экземпляр моего провайдера.

Как мне получить реальный объект (aClass) лямбда-выражения?

Ответы [ 2 ]

12 голосов
/ 31 августа 2010

Это действительно возможно, но это зависит от того, что вы передаете в этот метод. Предположим, у вас есть сценарий, в котором вы передаете метод экземпляра класса, в котором вы находитесь, ProcessCommand:

public class TestClass
{
    public void TestMethod()
    {
        ProcessCommand(() => MethodToCall());
    }
    public bool MethodToCall() { return true; }
    void ProcessCommand(Expression<Func<bool>> expression) { ... }
}

Тогда вы можете использовать следующий метод ProcessCommand. Это работает только потому, что MethodToCall вызывается в этом экземпляре.

void ProcessCommand(Expression<Func<bool>> expression)
{
    var lambda = (LambdaExpression) expression;
    var methodCall = (MethodCallExpression) lambda.Body;
    var constant = (ConstantExpression) methodCall.Object;
    var myObject = constant.Value;
}

Более сложный сценарий выглядит следующим образом:

public class CallingClass
{
    public void TestMethod()
    {
        var calledClass = new CalledClass();
        ProcessCommand(() => calledClass.MethodToCall());
    }
    void ProcessCommand(Expression<Func<bool>> expression) { ... }
}
public class CalledClass
{
    public bool MethodToCall() { return true; }
}

Метод, который мы вызываем, теперь находится в другом классе и вызывается не в этом экземпляре, а в экземпляре CalledClass с именем calledClass. Но как компилятор передает переменную calledClass в лямбда-выражение? Нет ничего, что определяло бы поле calledClass, к которому может быть вызван метод MethodToCall.

Компилятор решает эту проблему, генерируя внутренний класс с одним полем с именем calledClass. В результате метод ProcessCommand теперь становится таким:

public void ProcessCommand(Expression<Func<bool>> expression)
{
    // The expression is a lambda expression with a method call body.
    var lambda = (LambdaExpression) expression;
    var methodCall = (MethodCallExpression) lambda.Body;
    // The method is called on a member of some instance.
    var member = (MemberExpression) methodCall.Object;
    // The member expression contains an instance of the anonymous class that
    // defines the member...
    var constant = (ConstantExpression) member.Expression;
    var anonymousClassInstance = constant.Value;
    // ...and the member itself.
    var calledClassField = (FieldInfo) member.Member;
    // With an instance of the anonymous class and the field, we can get its value.
    var calledClass =
        (CalledClass) calledClassField.GetValue(anonymousClassInstance);
}

Немного сложнее, потому что компилятор должен генерировать анонимный внутренний класс.

2 голосов
/ 31 августа 2010

Это невозможно "из коробки", вы можете взломать что-то с помощью отражения, , но это не рекомендуется, это будет очень задом наперед.
Редактировать: На самом деле возможно, согласно Рональду, но все же довольно далеко назад.Скрытые побочные эффекты, подобные этому, затрудняют чтение и поддержку кода.

Вместо этого ваша ProcessCommand должна использовать либо весь объект aClass, либо, более предпочтительно, интерфейс IMyCommand с .Method(), а также дополнительные методы и свойствачто нужно ProcessCommandТогда тип aClass.GetType() должен реализовать IMyCommand.

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