给出以下一组课程:
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
只会使用一个参数。修改了代码来表达这一点。
答案 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);
}
}