从MethodCallExpression访问调用对象

时间:2008-12-04 13:10:33

标签: c#

我正在尝试了解表达式树,并且我创建了一个采用

的方法
Expression<Func<bool>> 

并在满足某些条件时执行它 - 请参阅下面的代码。

        private static void TryCommand(Expression<Func<bool>> expression)
        {
            var methodCallExpression = expression.Body as MethodCallExpression;
            if (methodCallExpression == null)
            {
                throw new ArgumentException("expression must be a MethodCallExpression.");
            }

            if (methodCallExpression.Object.Type != typeof (MyClass))
            {
                throw new ArgumentException("expression must be operating on an instanceof MyClass.");                
            }

            var func = expression.Compile();
            var success = func.Invoke();

            if(!success)
            {
                Console.WriteLine(methodCallExpression.Method.Name + "() failed with error code " + (func.Target as MyClass).GetError());
            }
        }

问题

(func.Target as MyClass) 

为空。显然我做错了!如何访问该方法正在运行的实例?

3 个答案:

答案 0 :(得分:3)

Akash,一旦你有一个MethodCallExpression,恢复方法调用者很简单。 您必须恢复MemberExpression并构建一个表达式树来评估它。

请参阅以下代码:


MethodCallExpression methodCallExpression = (MethodCallExpression)expression.Body;
MemberExpression memberExpression = (MemberExpression)methodCallExpression.Object;

Expression<Func<Object>> getCallerExpression = Expression<Func<Object>>.Lambda<Func<Object>>(memberExpression);
Func<Object> getCaller = getCallerExpression.Compile();
MyClass caller = (MyClass)getCaller();

希望这有帮助,

Ricardo Lacerda Castelo Branco

答案 1 :(得分:1)

方法调用的目标是MyClass的实例,但委托本身不是方法调用。它是在执行时执行方法调用的东西。

如果你看一下func.Target,你会发现它是System.Runtime.CompilerServices.ExecutionScope

现在你可以测试它,投射它,然后获取Locals或Globals(不确定是哪个)来获取目标。但是,我怀疑只更改为使用Func<int>(或任何类型的错误代码)并在首先执行委托时返回错误代码会更简洁。那你甚至不需要表达式树。

编辑:鉴于您的意见,我建议:

public static void TryCommand(Expression<Func<MyClass,bool>> command,
                              MyClass c)
{
    // Code as before to find the method name etc.

    Func<MyClass, bool> compiled = command.Compile();

    if (!compiled(c))
    {
        Console.WriteLine(methodCallExpression.Method.Name
            + "() failed with error code " + c.GetError());
    }
}

然后你用:

来调用它
TryCommand(x => x.SomeMethod(), myClass);

答案 2 :(得分:1)

目标为null,因为该方法是静态的。在反射中静态MethodInfo上的Invoke(..)将忽略目标。这可能是一种扩展方法,在这种情况下,第一个参数是推断目标。

由于大多数LINQ都基于扩展方法,因此您会看到这种情况经常发生反射。