C# 获取对作为Func传递的Lambda中参数的引用

C# 获取对作为Func传递的Lambda中参数的引用,c#,lambda,func,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() {

给定以下一组类:

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'.
    }
}
公共类MyClass
{
公共int MyInt{get;set;}
}    
公共类对象处理器
{
公共int ProcessObject(MyClass MyClass)
{
返回myClass.MyInt++;
} 
}
公开课跑者
{
公开募捐
{
var classToPass=new MyClass();
FuncExecutor.ExecuteAction(x=>x.ProcessObject(classToPass));
}
}
公共静态类执行器
{
公共静态void执行(表达式)
{
//var func=expression.Compile();…使用表达式有帮助吗?
//在这一点上,我如何获得对“classToPass”的引用?
//“classToPass”类型已知为“T”,在本例中为“MyClass”。
}
}
ExecuteAction
方法中,如何获取传递到
ProcessObject
classToPass
实例的引用

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

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

  • ProcessObject
    将只接受一个参数
  • 参数类型是预先知道的

代码被修改以表达这一点。

非常具体地回答:

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"
    }
}
公共类运行程序
{
公开募捐
{
var classToPass=new MyClass();
classToPass.MyInt=42;
FuncExecutor.ExecuteAction(x=>x.ProcessObject(classToPass));
}
}
公共类函数执行器
{
公共静态void执行(表达式)
{
var lambdaExpression=(lambdaExpression)表达式;
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);//打印“42”
}
}
请注意,当您将lambda传递给
ExecuteAction
方法时,将捕获一个局部变量引用(
classToPass
)。编译器将生成一些代码来正确处理该问题。更准确地说,它将生成一个类型为
MyClass
的单个成员(字段)的类型,以保存引用并从此点开始使用它。这就是为什么在参数表达式列表中会出现
MemberExpression

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

我不会依赖这个代码


您可以在此处阅读有关lambda相关编译器生成代码的更多信息,例如:

最简单的方法是将实例作为参数传递,并让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);
    }
}
公共接口IObjectProcessor{
公共int ProcessObject(T实例);
}
公共类MyClassProcessor:IObjectProcessor{
公共int ProcessObject(MyClass MyClass){
返回myClass.MyInt++;
}
}
公开课跑者{
公开募捐{
var classToPass=new MyClass();
var processor=新的MyClassProcessor();
FuncExecutor.ExecuteAction(处理器,classToPass);
}
}
公共类函数执行器{
公共静态void执行(IObjectProcessor,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);
    }
}
公共类MyClassProcessor
公共静态int ProcessObject(MyClass MyClass){
返回myClass.MyInt++;
}
}
公开课跑者{
公开募捐{
var classToPass=new MyClass();
FuncExecutor.ExecuteAction(MyClassProcessor.ProcessObject,classToPass);
}
}
公共类函数执行器{
公共静态无效执行(Func进程,T对象){
int结果=过程(obj);
}
}

您可以在表达式树中爬行,找到
常量表达式
。如何做到这一点取决于树的结构可能会有多大的变化。这可能是一种情况,在这种情况下,完全不同的方法可能比试图使用表达式树强制解决方案更好。@Dirk,你能进一步解释一下你的意思吗?它通常被称为。与其问问题,不如问你认为什么是好的解决方案。当然,这只是我基于小代码示例的观点。这与我最终所做的非常相似,直到一个基于纯表达式的解决方案发布在另一个答案中。