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

时间:2014-02-05 14:23:55

标签: c# lambda func

给出以下一组课程:

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'.
    }
}

ExecuteAction方法中,如何获取传递给classToPass的{​​{1}}实例的引用?

编辑:这些评论强调了尝试解析表达式树的复杂性,表达式树的组成可能差异很大。

然而,在这种特殊情况下,有两个事实可以大大减少这种变化:

  • ProcessObject只会使用一个参数。
  • 参数类型事先已知。

修改了代码来表达这一点。

2 个答案:

答案 0 :(得分:1)

非常具体地回答:

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"
    }
}

请注意,当您将lambda传递给ExecuteAction方法时,您将捕获局部变量引用(classToPass)。编译器将生成一些代码来正确处理。更准确地说,它将生成一个类型为MyClass的单个成员(字段)的类型来保存引用并从此处使用它。这就是为什么你会在参数表达式列表中得到MemberExpression

由于您无法直接操作此生成的类型,因此不能仅使用成员表达式Value属性。但您可以使用MemberInfo和目标引用(编译器生成类型的实例)动态调用成员访问器。

我不会依赖此代码。

您可以在此处阅读有关lambda相关编译器生成代码的更多信息,例如:http://thewalkingdev.blogspot.fr/2012/04/c-lambda-expressions-and-closures.html

答案 1 :(得分:1)

最简单的方法是将实例作为参数传递,让ExecuteAction负责使用该实例调用process方法。要做到这一点,有必要使用通用对象处理器接口为您的代码提供一点结构:

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

这种设计可能有点烦人,特别是如果你的处理器是“无状态”的,并且你真的需要一个Func作为参数。在这种情况下,您可以删除接口并使用静态处理器:

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);
    }
}
相关问题