You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
128 lines
3.6 KiB
128 lines
3.6 KiB
using System.Reflection;
|
|
using Connected.ServiceModel;
|
|
|
|
namespace Connected.Interop;
|
|
|
|
public static class Methods
|
|
{
|
|
/// <summary>
|
|
/// Resolves method based on generic arguments and parameter types.
|
|
/// </summary>
|
|
/// <param name="type">The type on which method is declared.</param>
|
|
/// <param name="name">The name of the method.</param>
|
|
/// <param name="typeArguments">The type arguments if a method is a generic method definition.</param>
|
|
/// <param name="parameterTypes">The argument types which method accepts.</param>
|
|
/// <returns></returns>
|
|
public static MethodInfo? ResolveMethod(this Type type, string name, Type[]? typeArguments, Type[]? parameterTypes)
|
|
{
|
|
var typeArgumentCount = typeArguments is not null ? typeArguments.Length : 0;
|
|
/*
|
|
* First, get all methods available on the type.
|
|
*/
|
|
foreach (var method in type.GetInheritedMethods())
|
|
{
|
|
/*
|
|
* If a method is a generic method and type arguments weren't passed,
|
|
* skip this method because we are not interested in it.
|
|
*/
|
|
if (method.IsGenericMethodDefinition != typeArgumentCount > 0)
|
|
continue;
|
|
/*
|
|
* Also, the name of the method must match, of course.
|
|
*/
|
|
if (!string.Equals(method.Name, name))
|
|
continue;
|
|
/*
|
|
* Now check if the type arguments match.
|
|
*/
|
|
if (method.IsGenericMethodDefinition && typeArguments is not null && typeArguments.Any())
|
|
{
|
|
/*
|
|
* Check if the number of arguments equals on both the definition and typeArguments argument.
|
|
*/
|
|
if (method.GetGenericArguments().Length != typeArgumentCount)
|
|
continue;
|
|
/*
|
|
* Now try to create a generic method definition.
|
|
*/
|
|
var constructedMethod = method.MakeGenericMethod(typeArguments);
|
|
/*
|
|
* And if parameters also match we have a target.
|
|
*/
|
|
if (ParametersMatch(constructedMethod.GetParameters(), parameterTypes))
|
|
return constructedMethod;
|
|
}
|
|
/*
|
|
* The method is not a generic method, we're only going to check the parameters types.
|
|
*/
|
|
if (ParametersMatch(method.GetParameters(), parameterTypes))
|
|
return method;
|
|
}
|
|
|
|
return default;
|
|
}
|
|
|
|
internal static bool ParametersMatch(ParameterInfo[] parameters, Type[] parameterTypes)
|
|
{
|
|
/*
|
|
* Parameterless service methods must pass IDto argument into api methods. First check this.
|
|
*/
|
|
if (parameterTypes is not null && parameterTypes.Length == 1 && parameterTypes[0] == typeof(Dto) && (parameters is null || parameters.Length == 0))
|
|
return true;
|
|
|
|
if (parameters.Length != parameterTypes.Length)
|
|
return false;
|
|
|
|
for (var i = 0; i < parameters.Length; i++)
|
|
{
|
|
if (!parameters[i].ParameterType.IsAssignableFrom(parameterTypes[i]))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public static IEnumerable<MethodInfo> GetInheritedMethods(this Type type)
|
|
{
|
|
foreach (var info in type.GetInheritedTypeInfos())
|
|
{
|
|
foreach (var p in info.GetRuntimeMethods())
|
|
yield return p;
|
|
}
|
|
}
|
|
|
|
public static async Task<object> InvokeAsync(this MethodInfo method, object component, params object[] parameters)
|
|
{
|
|
if (method.ReturnType is null)
|
|
{
|
|
method.Invoke(component, parameters);
|
|
|
|
return Task.FromResult<object>(null);
|
|
}
|
|
else
|
|
{
|
|
var isAwaitable = method.ReturnType.GetMethod(nameof(Task.GetAwaiter)) is not null;
|
|
|
|
if (isAwaitable)
|
|
{
|
|
if (method.ReturnType.IsGenericType)
|
|
return await (dynamic)method.Invoke(component, parameters);
|
|
else
|
|
{
|
|
await (Task)method.Invoke(component, parameters);
|
|
return null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (method.ReturnType == typeof(void))
|
|
method.Invoke(component, parameters);
|
|
else
|
|
return Task.FromResult(method.Invoke(component, parameters));
|
|
}
|
|
}
|
|
|
|
return Task.FromResult<object>(null);
|
|
}
|
|
}
|