using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace TeamAAS.Modularity
{
///
/// 通用程序集/模块扫描器:引擎、相机、机器人、通讯、供料器、插件等模块的
/// 类型发现统一走这里(原先每个 Manager 各写一套 AppDomain 反射)。
///
/// - 扫描范围:AppDomain 已加载的全部程序集(主程序直接引用的 + PluginLoader
/// 从 Runtime\Plugins 动态加载的插件 DLL;插件加载先于各模块的 Discover);
/// - 类型解析容错:程序集部分类型加载失败(依赖缺失)时保留其余可用类型;
/// - 结果缓存:按接口类型缓存,程序集数量变化(新插件加载)自动重扫。
///
public static class AssemblyScanner
{
private static readonly object _sync = new object();
/// 类型缓存:接口类型 → 实现类型列表
private static readonly Dictionary> _cache = new Dictionary>();
/// 程序集数量指纹(变化 = 有新程序集加载,缓存失效)
private static int _assemblyStamp = -1;
/// 枚举 AppDomain 全部程序集
public static IEnumerable GetAllAssemblies()
=> AppDomain.CurrentDomain.GetAssemblies();
/// 枚举全部具体类型(容错:ReflectionTypeLoadException 时保留可用类型)
public static IEnumerable GetAllTypes()
{
foreach (var asm in GetAllAssemblies())
{
Type[] types;
try { types = asm.GetTypes(); }
catch (ReflectionTypeLoadException ex)
{
types = ex.Types?.Where(t => t != null).ToArray() ?? Type.EmptyTypes;
}
catch { continue; }
foreach (var t in types)
if (t != null) yield return t;
}
}
///
/// 查找实现 TInterface 的全部具体类型(非抽象/非接口/非泛型定义;带缓存)。
/// 插件 DLL 由 PluginLoader 在启动时加载进 AppDomain,新插件加载后缓存自动失效重扫。
///
public static IReadOnlyList FindImplementations() where T : class
{
lock (_sync)
{
var stamp = GetAllAssemblies().Count();
if (_assemblyStamp != stamp || !_cache.TryGetValue(typeof(T), out var list))
{
list = GetAllTypes()
.Where(t => !t.IsAbstract && !t.IsInterface && !t.IsGenericTypeDefinition
&& typeof(T).IsAssignableFrom(t))
.Distinct()
.ToList();
_cache[typeof(T)] = list;
_assemblyStamp = stamp;
}
return list;
}
}
///
/// 创建 TInterface 的全部实例(无参构造,构造失败跳过并记日志)。
/// 业务键去重(如 EngineName)由调用方负责。
///
public static IReadOnlyList CreateInstances() where T : class
{
var result = new List();
foreach (var type in FindImplementations())
{
try
{
if (Activator.CreateInstance(type) is T instance)
result.Add(instance);
}
catch (Exception ex)
{
// 构造失败(SDK 运行库缺失等)→ 跳过该实现;
// 记日志便于诊断"引擎没起来/主页画面空白"这类静默降级问题
try { AppLogger.Warning($"模块 {type.FullName} 实例化失败,已跳过: {ex.Message}", "模块扫描"); } catch { }
}
}
return result;
}
/// 清空缓存(强制下次重扫;一般无需调用)
public static void InvalidateCache()
{
lock (_sync)
{
_cache.Clear();
_assemblyStamp = -1;
}
}
}
}