[英]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
方法中,如何獲取對傳遞給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"
}
}
請注意,當您將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.