【问题标题】:How to get the instance of a referred instance from a lambda expression如何从 lambda 表达式中获取引用实例的实例
【发布时间】:2010-08-31 08:53:55
【问题描述】:

我有这个 lambda 表达式 Expression<Func<bool>> commandToExecute

然后我用一个方法传递一个类的实例:

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

如何在ProcessCommand 方法中获取aClass 的实例?

我想执行这个类的一些附加方法或获取一些属性值。

这可能吗?

编辑: 我现在编写了一个简单的静态辅助方法来获取实例:

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 窗体的实例。

如何获取 lambda 表达式的真实对象 (aClass)?

【问题讨论】:

    标签: c# delegates lambda


    【解决方案1】:

    这实际上是可能的,但这取决于您传递给此方法的内容。假设您有将您所在类的实例方法传递给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; }
    }
    

    我们正在调用的方法现在在另一个类中,并且不是在此实例上调用,而是在称为calledClassCalledClass 实例上调用。但是编译器如何将calledClass 变量传递给lambda 表达式呢?没有定义可以调用方法MethodToCall 的字段calledClass

    编译器通过生成一个包含一个名为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】:

    不可能“开箱即用”,您可以通过反射破解某些东西,但这是不可取的,它会非常落后。
    编辑: 根据罗纳德的说法实际上是可能的,但仍然相当落后。像这样隐藏的副作用使代码难以阅读和维护。

    相反,您的 ProcessCommand 应该采用整个 aClass 对象,或者更好地采用带有 .Method()IMyCommand 接口以及 ProcessCommand 需要的其他方法和属性。那么aClass.GetType() 类型应该实现IMyCommand

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-07-02
      • 2023-04-06
      • 2021-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-05-14
      相关资源
      最近更新 更多