使用反射在方法中列出(检测)闭包。

4
我知道可以使用MethodInfo.GetMethodBody().LocalVariables迭代方法中所有本地定义的变量。
但是如果我定义一个方法如下:
public static void somemethod( int someint )
{
    int test = 1;

    HM.M(() =>
    {
        test = 2;
    });
}

匿名方法可以作为闭包访问someint和test,但GetMethodBody().LocalVariables将为空。
有没有一种方法可以使用反射来检测闭包?

如果您检查IL,您会发现编译器创建了一个编译器生成的类的实例,例如Program/'<>c__DisplayClass1',但是Reflection并没有提供这种工具。您可能可以使用Mono.Cecil或System.Reflection.Metadata来完成这个任务。 - Mike Zboray
我已经对第一个进行了一些研究,到目前为止,我认为Mono.Cecil无法实现它。我会进一步研究,并且也会查看System.Reflection.Metadata,谢谢。 - Casson
1个回答

2
我已经找到答案。在C#中,闭包是由编译器通过生成一个类来实现的。编译器会生成一个类,在该类中复制所有在方法中可访问的变量,并将方法本身封装在该类中。外部变量的值被复制到该类中。
因此,在我上面的示例中,M方法具有以下签名:
public void M( Action method )

获取关闭清单的步骤是:
MethodInfo mi = method.Method; 

FieldInfo[] fields = mi.DeclaringType.GetFields
                                    (
                                        BindingFlags.NonPublic |
                                        BindingFlags.Instance |
                                        BindingFlags.Public |
                                        BindingFlags.Static
                                    );

DeclaringType将是编译器生成的用于实现闭包的类。

在fields数组中,所有闭包都将列出。

获取闭包的值是另一回事,但它不是这个问题的一部分。

编辑: 正如@leppie指出的那样,获取值很容易:

object res = fields[ 0 ].GetValue( method.Target );

其实很简单。只需检查委托的“Target”属性即可。您还可以从该实例获取值。 - leppie
在这种情况下,您可能想改进您的答案 :) - leppie
@leppie:由于我在问题中没有包含获取值,所以从技术上讲,答案是可以的。我需要一个闭包列表,因此在这种情况下使用DeclaringType或Target是等效的,但你是对的,改进答案不会有害。 - Casson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接