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,你能进一步解释一下你的意思吗?它通常被称为。与其问问题,不如问你认为什么是好的解决方案。当然,这只是我基于小代码示例的观点。这与我最终所做的非常相似,直到一个基于纯表达式的解决方案发布在另一个答案中。