Question

How does one determine if a MethodInfo represents the metadata for a lambda expression?

Was it helpful?

Solution

I think you are talking about anonymous methods.So, you can write an extension method for that and check whether the name of the method contains any invalid chars.Because the compiler generated methods contain invalid chars, you can use that feature to determine whether the method is anonymous or not:

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

Test:

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

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

More elegant way would be validating the method name using IsValidLanguageIndependentIdentifier method, like this (method from this answer):

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

Remember in order to access IsValidLanguageIndependentIdentifier method you need to include the System.CodeDom.Compiler namespace.

OTHER TIPS

The following code can do the trick. It is a bit long compared to the accepted answer, but alas the accepted answer does not make a proper distinction between lambdas and inner methods, which both get name mangled by the compiler. Hence the following provide two methods: IsAnonymous and IsInner.

By the way, the code should work under Mono as well (names seem to be mangled the same way but with a different magic tag under the hood).

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());
    }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top