如何用反射识别lambda闭包

时间:2014-09-08 10:01:33

标签: c# reflection lambda delegates

我正在编写一个涉及Actions的组件,当Action.Target对象是编译器生成的闭包时,需要找到一种方法来识别使用反射的情况。我正在做一个尝试找到方法的实验,这个小实验的目的是开发一个谓词,它接受一个Action并返回一个bool,告诉动作目标是否是这种闭包类的一个实例。

在我的测试用例中,我有以下方法可以创建4种不同类型的操作:

    private void _createClosure(int i)
    {
        ClosureAction = new Action(() =>
        {
            var j = i;
            var k = somenum;
        });
    }

    private void _createLambda()
    {
        LambdaAction = new Action(() =>
        {
            this._instanceAction();

        });
    }

    private void _createInstance()
    {
        InstanceAction = new Action(_instanceAction);
    }

    private void _createStatic()
    {
        StaticAction = new Action(_staticAction);
    }

    private int somenum;

    private void _instanceAction()
    {
        somenum++;
    }

    private static void _staticAction()
    {

    }

下表显示了每个操作的属性: Action properties

如你所见,LambaAction和ClosureAction在定义方面非常相似,它们都使用lambda,但是闭包有一个在lambda中使用的本地函数变量,因此编译器被强制生成一个闭包类。很明显,第二行,即呈现ClosureAction的那一行,是唯一一个具有闭包类型的目标。静态的根本没有目标,另外两个使用调用类(Called ActionReferences)作为目标。下表显示了目标反射类型属性的比较: Target Types

因此,我们可以看到关闭案例的独特之处在于目标类型不是类型信息,而是嵌套类型。它也是唯一一个私有嵌套,密封并且名称包含字符串+<> c__DisplayClass的人。虽然我认为这些特性对于任何正常的用例都是决定性的,但我更愿意定义一个我可以依赖的谓词。我不想将此机制建立在编译器命名约定或属性上,这些机制不是唯一的,因为从技术上讲,用户可能会创建一个具有相同命名约定的私有嵌套密封类......它不太可能,但是它不是100%清洁的解决方案。

最后 - 问题是:是否有一种简洁的方法来编写谓词,识别实际上是编译器生成的闭包的动作?

由于

1 个答案:

答案 0 :(得分:3)

这不是100%准确,但通常有效:

bool isClosure = action.Target != null && Attribute.IsDefined(
    action.Target.GetType(), typeof(CompilerGeneratedAttribute));
Console.WriteLine(isClosure);

您当然可以通过手动将[CompilerGenerated]添加到您选择的任何类型来强制误报。

您也可以使用action.Method.DeclaringType,但由于所有捕获涉及目标实例,因此保留Target检查非常有用:

bool isClosure = action.Target != null && Attribute.IsDefined(
    action.Method.DeclaringType, typeof(CompilerGeneratedAttribute));