Making a better RunGeneric method with more checks & more exceptions

This commit is contained in:
Zoe Roux 2021-02-17 01:09:49 +01:00
parent 07aa4d6752
commit 8597cf34f2

View File

@ -229,6 +229,49 @@ namespace Kyoo
return types.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType); return types.FirstOrDefault(x => x.IsGenericType && x.GetGenericTypeDefinition() == genericType);
} }
public static IEnumerable<T> IfEmpty<T>(this IEnumerable<T> self, Action action)
{
using IEnumerator<T> enumerator = self.GetEnumerator();
if (!enumerator.MoveNext())
{
action();
yield break;
}
while (enumerator.MoveNext())
yield return enumerator.Current;
}
private static MethodInfo GetMethod(Type type, BindingFlags flag, string name, Type[] generics, object[] args)
{
MethodInfo[] methods = type.GetMethods(flag | BindingFlags.Public | BindingFlags.NonPublic)
.Where(x => x.Name == name)
.Where(x => x.GetGenericArguments().Length == generics.Length)
.Where(x => x.GetParameters().Length == args.Length)
.IfEmpty(() => throw new NullReferenceException($"A method named {name} with " +
$"{args.Length} arguments and {generics.Length} generic " +
$"types could not be found on {type.Name}."))
.Where(x =>
{
int i = 0;
// TODO this thing does not work.
return x.GetGenericArguments().All(y => y.IsAssignableFrom(generics[i++]));
})
.IfEmpty(() => throw new NullReferenceException($"No method {name} match the generics specified."))
.Where(x =>
{
int i = 0;
return x.GetParameters().All(y => y.ParameterType == args[i++].GetType());
})
.IfEmpty(() => throw new NullReferenceException($"No method {name} match the parameters's types."))
.Take(2)
.ToArray();
if (methods.Length == 1)
return methods[0];
throw new NullReferenceException($"Multiple methods named {name} match the generics and parameters constraints.");
}
public static T RunGenericMethod<T>( public static T RunGenericMethod<T>(
[NotNull] Type owner, [NotNull] Type owner,
[NotNull] string methodName, [NotNull] string methodName,
@ -252,10 +295,7 @@ namespace Kyoo
throw new ArgumentNullException(nameof(types)); throw new ArgumentNullException(nameof(types));
if (types.Length < 1) if (types.Length < 1)
throw new ArgumentException($"The {nameof(types)} array is empty. At least one type is needed."); throw new ArgumentException($"The {nameof(types)} array is empty. At least one type is needed.");
MethodInfo method = owner.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) MethodInfo method = GetMethod(owner, BindingFlags.Static, methodName, types, args);
.SingleOrDefault(x => x.Name == methodName && x.GetParameters().Length == args.Length);
if (method == null)
throw new NullReferenceException($"A method named {methodName} with {args.Length} arguments could not be found on {owner.FullName}");
return (T)method.MakeGenericMethod(types).Invoke(null, args?.ToArray()); return (T)method.MakeGenericMethod(types).Invoke(null, args?.ToArray());
} }
@ -264,17 +304,24 @@ namespace Kyoo
[NotNull] string methodName, [NotNull] string methodName,
[NotNull] Type type, [NotNull] Type type,
params object[] args) params object[] args)
{
return RunGenericMethod<T>(instance, methodName, new[] {type}, args);
}
public static T RunGenericMethod<T>(
[NotNull] object instance,
[NotNull] string methodName,
[NotNull] Type[] types,
params object[] args)
{ {
if (instance == null) if (instance == null)
throw new ArgumentNullException(nameof(instance)); throw new ArgumentNullException(nameof(instance));
if (methodName == null) if (methodName == null)
throw new ArgumentNullException(nameof(methodName)); throw new ArgumentNullException(nameof(methodName));
if (type == null) if (types == null || types.Length == 0)
throw new ArgumentNullException(nameof(type)); throw new ArgumentNullException(nameof(types));
MethodInfo method = instance.GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); MethodInfo method = GetMethod(instance.GetType(), BindingFlags.Instance, methodName, types, args);
if (method == null) return (T)method.MakeGenericMethod(types).Invoke(instance, args?.ToArray());
throw new NullReferenceException($"A method named {methodName} could not be found on {instance.GetType().FullName}");
return (T)method.MakeGenericMethod(type).Invoke(instance, args?.ToArray());
} }
[NotNull] [NotNull]