确定MethodInfo是否表示lambda表达式

本文关键字:lambda 表达式 表示 是否 MethodInfo 确定 | 更新日期: 2023-09-27 17:59:20

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

确定MethodInfo是否表示lambda表达式

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

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方法验证方法名称,如下所示(来自该答案的方法):

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

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

下面的代码可以完成这个技巧。与公认的答案相比,它有点长,但遗憾的是,公认的答案并没有正确区分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());
    }
}