AssemblyScanner.cs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Reflection;
  5. namespace TeamAAS.Modularity
  6. {
  7. /// <summary>
  8. /// 通用程序集/模块扫描器:引擎、相机、机器人、通讯、供料器、插件等模块的
  9. /// 类型发现统一走这里(原先每个 Manager 各写一套 AppDomain 反射)。
  10. ///
  11. /// - 扫描范围:AppDomain 已加载的全部程序集(主程序直接引用的 + PluginLoader
  12. /// 从 Runtime\Plugins 动态加载的插件 DLL;插件加载先于各模块的 Discover);
  13. /// - 类型解析容错:程序集部分类型加载失败(依赖缺失)时保留其余可用类型;
  14. /// - 结果缓存:按接口类型缓存,程序集数量变化(新插件加载)自动重扫。
  15. /// </summary>
  16. public static class AssemblyScanner
  17. {
  18. private static readonly object _sync = new object();
  19. /// <summary>类型缓存:接口类型 → 实现类型列表</summary>
  20. private static readonly Dictionary<Type, List<Type>> _cache = new Dictionary<Type, List<Type>>();
  21. /// <summary>程序集数量指纹(变化 = 有新程序集加载,缓存失效)</summary>
  22. private static int _assemblyStamp = -1;
  23. /// <summary>枚举 AppDomain 全部程序集</summary>
  24. public static IEnumerable<Assembly> GetAllAssemblies()
  25. => AppDomain.CurrentDomain.GetAssemblies();
  26. /// <summary>枚举全部具体类型(容错:ReflectionTypeLoadException 时保留可用类型)</summary>
  27. public static IEnumerable<Type> GetAllTypes()
  28. {
  29. foreach (var asm in GetAllAssemblies())
  30. {
  31. Type[] types;
  32. try { types = asm.GetTypes(); }
  33. catch (ReflectionTypeLoadException ex)
  34. {
  35. types = ex.Types?.Where(t => t != null).ToArray() ?? Type.EmptyTypes;
  36. }
  37. catch { continue; }
  38. foreach (var t in types)
  39. if (t != null) yield return t;
  40. }
  41. }
  42. /// <summary>
  43. /// 查找实现 TInterface 的全部具体类型(非抽象/非接口/非泛型定义;带缓存)。
  44. /// 插件 DLL 由 PluginLoader 在启动时加载进 AppDomain,新插件加载后缓存自动失效重扫。
  45. /// </summary>
  46. public static IReadOnlyList<Type> FindImplementations<T>() where T : class
  47. {
  48. lock (_sync)
  49. {
  50. var stamp = GetAllAssemblies().Count();
  51. if (_assemblyStamp != stamp || !_cache.TryGetValue(typeof(T), out var list))
  52. {
  53. list = GetAllTypes()
  54. .Where(t => !t.IsAbstract && !t.IsInterface && !t.IsGenericTypeDefinition
  55. && typeof(T).IsAssignableFrom(t))
  56. .Distinct()
  57. .ToList();
  58. _cache[typeof(T)] = list;
  59. _assemblyStamp = stamp;
  60. }
  61. return list;
  62. }
  63. }
  64. /// <summary>
  65. /// 创建 TInterface 的全部实例(无参构造,构造失败跳过并记日志)。
  66. /// 业务键去重(如 EngineName)由调用方负责。
  67. /// </summary>
  68. public static IReadOnlyList<T> CreateInstances<T>() where T : class
  69. {
  70. var result = new List<T>();
  71. foreach (var type in FindImplementations<T>())
  72. {
  73. try
  74. {
  75. if (Activator.CreateInstance(type) is T instance)
  76. result.Add(instance);
  77. }
  78. catch (Exception ex)
  79. {
  80. // 构造失败(SDK 运行库缺失等)→ 跳过该实现;
  81. // 记日志便于诊断"引擎没起来/主页画面空白"这类静默降级问题
  82. try { AppLogger.Warning($"模块 {type.FullName} 实例化失败,已跳过: {ex.Message}", "模块扫描"); } catch { }
  83. }
  84. }
  85. return result;
  86. }
  87. /// <summary>清空缓存(强制下次重扫;一般无需调用)</summary>
  88. public static void InvalidateCache()
  89. {
  90. lock (_sync)
  91. {
  92. _cache.Clear();
  93. _assemblyStamp = -1;
  94. }
  95. }
  96. }
  97. }