简体   繁体   English

获取对作为Func传递的Lambda内的参数的引用

[英]Get reference to parameter inside a Lambda passed as a Func

Given the following set of classes: 给定以下类:

public class MyClass
{
    public int MyInt { get; set; }
}    

public class ObjectProcessor
{
    public int ProcessObject(MyClass myClass)
    {
        return myClass.MyInt ++;
    } 
}

public class Runner
{
    public void Run()
    {
        var classToPass = new MyClass();

        FuncExecutor.ExecuteAction<MyClass>(x => x.ProcessObject(classToPass));
    }
}

public static class FuncExecutor
{
    public static void ExecuteAction<T>(Expression<Func<ObjectProcessor, int>> expression)
    {
        // var func = expression.Compile(); ... does having an Expression help?

        // How can I get a reference to 'classToPass' at this point?

        // The 'classToPass' Type is known to be 'T', in this case 'MyClass'.
    }
}

From within the ExecuteAction method, how can I get a reference to the classToPass instance that was passed in to ProcessObject ? ExecuteAction方法中,如何获取对传递给ProcessObjectclassToPass实例的引用?

EDIT: The comments have highlighted the complexity of trying to parse Expression Trees which could vary widely in their composition. 编辑:评论强调了尝试解析表达树的复杂性,表达树的组成可能有很大差异。

However, in this particular case there are two facts which cut down this variation considerably: 但是,在这种特殊情况下,有两个事实可以大大减少这种变化:

  • ProcessObject will only ever take a single parameter. ProcessObject只会采用一个参数。
  • The parameter type is known in advance. 参数类型是事先已知的。

Code altered to express this. 代码被改变以表达这一点。

To answer very specifically: 非常具体地回答:

public class Runner
{
    public void Run()
    {
        var classToPass = new MyClass();
        classToPass.MyInt = 42;

        FuncExecutor.ExecuteAction(x => x.ProcessObject(classToPass));
    }
}

public class FuncExecutor
{
    public static void ExecuteAction(Expression<Func<ObjectProcessor, int>> expression)
    {
        var lambdaExpression = (LambdaExpression)expression;
        var methodCallExpression = (MethodCallExpression)lambdaExpression.Body;

        var memberExpression = (MemberExpression)methodCallExpression.Arguments[0];
        var constantExpression = (ConstantExpression)memberExpression.Expression;
        var fieldInfo = (FieldInfo)memberExpression.Member;

        var myClassReference = (MyClass) fieldInfo.GetValue(constantExpression.Value);

        Console.WriteLine(myClassReference.MyInt); // prints "42"
    }
}

Please note that when you pass the lambda to the ExecuteAction method, you capture a local variable reference ( classToPass ). 请注意,当您将lambda传递给ExecuteAction方法时,您将捕获局部变量引用( classToPass )。 The compiler will generate some code to handle that properly. 编译器将生成一些代码来正确处理。 More precisely, it will generate a type with a single member (a field) of type MyClass to hold the reference and use it from this point. 更准确地说,它将生成一个具有MyClass类型的单个成员(字段)的类型来保存引用并从此处使用它。 That's why you'll get a MemberExpression in the argument expression list. 这就是为什么你会在参数表达式列表中得到一个MemberExpression

Since you can't directly manipulate this generated type, you can't just use the member expression Value property. 由于无法直接操作此生成的类型,因此不能仅使用成员表达式Value属性。 But you can dynamically invoke the member accessor using the MemberInfo and the target reference (an instance of the compiler generated type). 但您可以使用MemberInfo和目标引用(编译器生成类型的实例)动态调用成员访问器。

I would not rely on this code. 我不会依赖这个代码。

You can read more about lambda related compiler generated code here, for example: http://thewalkingdev.blogspot.fr/2012/04/c-lambda-expressions-and-closures.html 您可以在此处阅读有关lambda相关编译器生成代码的更多信息,例如: http//thewalkingdev.blogspot.fr/2012/04/c-lambda-expressions-and-closures.html

The easiest way is to pass the instance as parameter and let ExecuteAction take care of calling the process method using that instance. 最简单的方法是将实例作为参数传递,让ExecuteAction负责使用该实例调用process方法。 To do this it is necessary to give your code a little bit of structure using a generic object processor interface: 要做到这一点,有必要使用通用对象处理器接口为您的代码提供一些结构:

public interface IObjectProcessor<T> {
    public int ProcessObject(T instance);
}

public class MyClassProcessor : IObjectProcessor<MyClass> {
    public int ProcessObject(MyClass myClass) {
        return myClass.MyInt ++;
    }
}

public class Runner {
    public void Run() {
        var classToPass = new MyClass();
        var processor = new MyClassProcessor();

        FuncExecutor.ExecuteAction<MyClass>(processor, classToPass);
    }
}

public class FuncExecutor {
    public static void ExecuteAction<T>(IObjectProcessor<T> processor, T obj) {
        int result = processor.ProcessObject(obj);
    }
}

This design could be a little annoying especially if your processor are "stateless" and if you really need a Func as parameter. 这种设计可能有点烦人,特别是如果你的处理器是“无状态”的,并且如果你真的需要一个Func作为参数。 In this case you can drop the interface and use static processors: 在这种情况下,您可以删除接口并使用静态处理器:

public class MyClassProcessor
    public static int ProcessObject(MyClass myClass) {
        return myClass.MyInt ++;
    }
}

public class Runner {
    public void Run() {
        var classToPass = new MyClass();

        FuncExecutor.ExecuteAction<MyClass>(MyClassProcessor.ProcessObject, classToPass);
    }
}

public class FuncExecutor {
    public static void ExecuteAction<T>(Func<T, int> process, T obj) {
        int result = process(obj);
    }
}

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

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