You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

ReflectionUtils.cs 3.5 kB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. using Microsoft.Extensions.DependencyInjection;
  6. namespace Discord.Commands
  7. {
  8. internal static class ReflectionUtils
  9. {
  10. private static readonly TypeInfo _objectTypeInfo = typeof(object).GetTypeInfo();
  11. internal static T CreateObject<T>(TypeInfo typeInfo, CommandService commands, IServiceProvider services = null)
  12. => CreateBuilder<T>(typeInfo, commands)(services);
  13. internal static Func<IServiceProvider, T> CreateBuilder<T>(TypeInfo typeInfo, CommandService commands)
  14. {
  15. var constructor = GetConstructor(typeInfo);
  16. var parameters = constructor.GetParameters();
  17. var properties = GetProperties(typeInfo);
  18. return (services) =>
  19. {
  20. var args = new object[parameters.Length];
  21. for (int i = 0; i < parameters.Length; i++)
  22. args[i] = GetMember(commands, services, parameters[i].ParameterType, typeInfo);
  23. var obj = InvokeConstructor<T>(constructor, args, typeInfo);
  24. foreach(var property in properties)
  25. property.SetValue(obj, GetMember(commands, services, property.PropertyType, typeInfo));
  26. return obj;
  27. };
  28. }
  29. private static T InvokeConstructor<T>(ConstructorInfo constructor, object[] args, TypeInfo ownerType)
  30. {
  31. try
  32. {
  33. return (T)constructor.Invoke(args);
  34. }
  35. catch (Exception ex)
  36. {
  37. throw new Exception($"Failed to create \"{ownerType.FullName}\"", ex);
  38. }
  39. }
  40. private static ConstructorInfo GetConstructor(TypeInfo ownerType)
  41. {
  42. var constructors = ownerType.DeclaredConstructors.Where(x => !x.IsStatic).ToArray();
  43. if (constructors.Length == 0)
  44. throw new InvalidOperationException($"No constructor found for \"{ownerType.FullName}\"");
  45. else if (constructors.Length > 1)
  46. throw new InvalidOperationException($"Multiple constructors found for \"{ownerType.FullName}\"");
  47. return constructors[0];
  48. }
  49. private static System.Reflection.PropertyInfo[] GetProperties(TypeInfo ownerType)
  50. {
  51. var result = new List<System.Reflection.PropertyInfo>();
  52. while (ownerType != _objectTypeInfo)
  53. {
  54. foreach (var prop in ownerType.DeclaredProperties)
  55. {
  56. if (prop.SetMethod?.IsStatic == false && prop.SetMethod?.IsPublic == true && prop.GetCustomAttribute<DontInjectAttribute>() == null)
  57. result.Add(prop);
  58. }
  59. ownerType = ownerType.BaseType.GetTypeInfo();
  60. }
  61. return result.ToArray();
  62. }
  63. private static object GetMember(CommandService commands, IServiceProvider services, Type memberType, TypeInfo ownerType)
  64. {
  65. if (memberType == typeof(CommandService))
  66. return commands;
  67. if (memberType == typeof(IServiceProvider) || memberType == services.GetType())
  68. return services;
  69. var service = services?.GetService(memberType);
  70. if (service != null)
  71. return service;
  72. throw new InvalidOperationException($"Failed to create \"{ownerType.FullName}\", dependency \"{memberType.Name}\" was not found.");
  73. }
  74. }
  75. }