确定MethodInfo是否表示lambda表达式

时间:2014-04-22 19:03:20

标签: c# reflection lambda

如何确定MethodInfo是否表示lambda表达式的元数据?

2 个答案:

答案 0 :(得分:4)

我认为你在谈论匿名方法。所以,你可以为它编写一个扩展方法,并检查方法的名称是否包含任何无效的chars。因为编译器生成的方法包含无效的chars,你可以使用该功能确定该方法是否是匿名的:

public static bool IsAnonymous(this MethodInfo method)
{
     var invalidChars = new[] {'<', '>'};
     return method.Name.Any(invalidChars.Contains);
}

测试:

Func<int> f = () => 23;

Console.Write(f.Method.IsAnonymous());  // true

更优雅的方法是使用IsValidLanguageIndependentIdentifier方法验证方法名称,如下所示(来自this的方法):

public static bool IsAnonymous(this MethodInfo method)
{
    return !CodeGenerator.IsValidLanguageIndependentIdentifier(method.Name);
}

请记住,要访问IsValidLanguageIndependentIdentifier方法,您需要包含System.CodeDom.Compiler命名空间。

答案 1 :(得分:0)

以下代码可以解决问题。与接受的答案相比,它有点长,但是可惜,接受的答案并未在lambda和内部方法之间进行适当的区分,它们都被编译器弄乱了名字。因此,以下提供了两种方法:IsAnonymousIsInner

顺便说一句,代码也应该在Mono下工作(名称似乎以相同的方式进行了修改,但引擎盖下的魔术标签不同)。

public static class MethodInfoUtil
{
    static readonly Regex MagicTagPattern = new Regex(">([a-zA-Z]+)__");
    static readonly string AnonymousMagicTag;
    static readonly string InnerMagicTag;

    public static bool IsAnonymous(this MethodInfo mi)
    {
        return mi.Name.Contains(AnonymousMagicTag);
    }

    public static bool IsInner(this MethodInfo mi)
    {
        return mi.Name.Contains(InnerMagicTag);
    }

    public static string GetNameMagicTag(this MethodInfo mi, bool noThrow = false)
    {
        var match = MagicTagPattern.Match(mi.Name);
        if (match.Success && match.Value is string value && !match.NextMatch().Success)
            return value;
        else if (noThrow)
            return null;
        else
            throw new ArgumentException($"Cant find magic tag of {mi}");
    }

    // static constructor: initialize the magic tags
    static MethodInfoUtil()
    {
        void Inner() { };
        Action inner = Inner;
        Action anonymous = () => { };
        InnerMagicTag = GetNameMagicTag(inner.Method);
        AnonymousMagicTag = GetNameMagicTag(anonymous.Method);

        CheckThatItWorks();
    }

    [Conditional("DEBUG")]
    static void CheckThatItWorks()
    { 
        // Static mathods are neither anonymous nor inner
        Debug.Assert(!((Func<int, int>)Math.Abs).Method.IsAnonymous());
        Debug.Assert(!((Func<int, int>)Math.Abs).Method.IsInner());

        // Instance methods are neither anonymous nor inner
        Debug.Assert(!((Func<string, bool>)"".StartsWith).Method.IsAnonymous());
        Debug.Assert(!((Func<string, bool>)"".StartsWith).Method.IsInner());

        // Lambda 
        Action anonymous1 = () => { };
        Debug.Assert(anonymous1.Method.IsAnonymous());
        Debug.Assert(!anonymous1.Method.IsInner());

        // Anonymous delegates 
        Action anonymous2 = delegate(){ };
        Debug.Assert(anonymous2.Method.IsAnonymous());

        // Sublambdas 
        Action anonymous3 = new Func<Func<Action>>(() => () => () => { })()();
        Debug.Assert(anonymous3.Method.IsAnonymous());

        void Inner() { }
        Action inner1 = Inner;
        Debug.Assert(inner1.Method.IsInner());
        Debug.Assert(!inner1.Method.IsAnonymous());

        // Deep inner methods have same tag as inner
        Action Imbricated()
        {
            void Inside() { };
            return Inside;
        }
        Action inner2 = Imbricated();
        Debug.Assert(inner2.Method.IsInner());
    }
}