Created
December 12, 2019 09:41
-
-
Save lbmaian/9daf96b4aa4db1970d5b608f0401df69 to your computer and use it in GitHub Desktop.
Helper extension methods for getting lambda/iterator compiler-generated methods
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class TypeExtensions | |
{ | |
const BindingFlags lambdaMethodBindingFlags = BindingFlags.Instance | BindingFlags.NonPublic; | |
// Note: In .NET Framework 3.5 and below, type parameter T in IEnumerable<T> is not covariant, i.e. | |
// an IEnumerable<MethodInfo> is not an IEnumerable<MethodBase>. Since this is used in HarmonyTargetMethods-annotated | |
// methods, which must have IEnumerable<MethodBase>, we must return IEnumerable<MethodBase> here as well. | |
public static IEnumerable<MethodBase> FindLambdaMethods(this Type targetType, Func<MethodInfo, bool> methodMatcher) | |
{ | |
// Lambda code is in compiler-generated non-public instance methods on either the target type (if doesn't need closure) | |
// or compiler-generated non-public nested classes (if needs closure). | |
foreach (var type in targetType.GetNestedTypes(BindingFlags.NonPublic).Prepend(targetType)) | |
{ | |
foreach (var method in type.GetMethods(lambdaMethodBindingFlags)) | |
{ | |
if (methodMatcher(method)) | |
yield return method; | |
} | |
} | |
} | |
const BindingFlags moveNextMethodBindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public; | |
public static MethodInfo FindIteratorMethod(this Type targetType, Func<Type, bool> enumeratorTypeMatcher) | |
{ | |
// Iterator code is in a MoveNext method on compiler-generated non-public nested class that implements IEnumerable. | |
// The MoveNext method may be either public or non-public, depending on the compiler. | |
foreach (var type in targetType.GetNestedTypes(BindingFlags.NonPublic)) | |
{ | |
if (typeof(IEnumerator).IsAssignableFrom(type) && enumeratorTypeMatcher(type)) | |
return type.GetMethod(nameof(IEnumerator.MoveNext), moveNextMethodBindingFlags); | |
} | |
throw new ArgumentException($"targetType ({targetType}) did not contain a matching iterator method"); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment