确定MethodInfo是否表示lambda表达式
本文关键字:lambda 表达式 表示 是否 MethodInfo 确定 | 更新日期: 2023-09-27 17:59:20
如何确定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和内部方法,这两种方法都会被编译器篡改名称。因此,以下提供了两种方法:IsAnonymous
和IsInner
。
顺便说一句,代码也应该在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());
}
}