簡體   English   中英

獲取對作為Func傳遞的Lambda內的參數的引用

[英]Get reference to parameter inside a Lambda passed as a 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方法中,如何獲取對傳遞給ProcessObjectclassToPass實例的引用?

編輯:評論強調了嘗試解析表達樹的復雜性,表達樹的組成可能有很大差異。

但是,在這種特殊情況下,有兩個事實可以大大減少這種變化:

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

請注意,當您將lambda傳遞給ExecuteAction方法時,您將捕獲局部變量引用( classToPass )。 編譯器將生成一些代碼來正確處理。 更准確地說,它將生成一個具有MyClass類型的單個成員(字段)的類型來保存引用並從此處使用它。 這就是為什么你會在參數表達式列表中得到一個MemberExpression

由於無法直接操作此生成的類型,因此不能僅使用成員表達式Value屬性。 但您可以使用MemberInfo和目標引用(編譯器生成類型的實例)動態調用成員訪問器。

我不會依賴這個代碼。

您可以在此處閱讀有關lambda相關編譯器生成代碼的更多信息,例如: http//thewalkingdev.blogspot.fr/2012/04/c-lambda-expressions-and-closures.html

最簡單的方法是將實例作為參數傳遞,讓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);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM