繁体   English   中英

如何找到尚未在AppDomain中加载的类型?

[英]How to find a type that is not loaded yet in AppDomain?

我正在使用WPF和Prism开发模块化应用程序。
我所有的UserControl都有单独的程序集并实现IUserControl接口。
我想以这种方式列出所有实现IUserControl接口的类型,这些类型构成一个加载的模块库。

//ModuleA.cs
var interfaceType = typeof(IUserControl);
var userControlTypes = AppDomain.CurrentDomain.GetAssemblies()
            .SelectMany(s => s.GetTypes())
            .Where(p => interfaceType.IsAssignableFrom(p) && p.IsClass);

但是我无法在userControlTypes列表中看到所有实现IUserControl的UserControl类型。
当我在Bootstrapper.cs中使用实现IUserControl的所有类时,如下所示;

var userControlTypes = new List<Type>()
{ 
      {typeof(HastaKayitControl)},
      {typeof(ViziteUserControl)},
      {typeof(DenemeUserControl)},
      ...
};

我可以从上面我上面写的列表(userControlTypes)中获得所有需要的UserControls。
这背后的原因是什么?

仅供参考:

  • 所有程序集都针对相同的.NET Framework版本。
  • 我的棱镜版本是6.1.0
  • 我将使用userControlTypes向最终用户显示应用程序内的所有UserControl类型。
  • IUserControl接口不包含任何内容。

此行为是设计使然。 .net CLR不会在程序集中加载,除非它被调用/输入强制将其加载。 想象一下,如果应用程序启动时目录中的每个.dll文件都被加载到内存中,而不是首次在运行时引用类型,则运行该应用程序的启动成本,某些具有大型库的应用程序的加载时间为分钟(甚至更多?)。 同样,这是不现实的,因为某些类型被解析为执行文件夹之外的库,例如解析为GAC的程序集。

在您的第一个示例AppDomain.CurrentDomain.GetAssemblies它将仅返回该应用程序域中已加载的程序集,而不是所有程序集。 要查看此内容,您可以添加{typeof(ViziteUserControl)}取自下一个代码部分 )并将其放置在其上方,这将强制CLR加载类型(和包含程序集),现在它( 包含组装 )也将返回AppDomain.CurrentDomain.GetAssemblies

在您的下一个代码片段中,您的代码将明确输入这些程序集并添加类型。 我认为这不需要任何解释。

因此,如果您希望AppDomain.CurrentDomain.GetAssemblies跨应用程序加载所有类型,则需要强制将程序集加载到内存(如果尚未加载)。 根据您的结构,您可以通过两种方法执行此操作。

  1. 遍历磁盘上的.dll文件(使用参考位置,例如Assembly.GetExecutingAssembly.Location ),然后调用Assembly.LoadFrom 使用通配符可确保仅加载程序集,而不是遇到的每个.dll库。
  2. 在配置文件中引用感兴趣的类型并从那里加载它们。 您可以使用Type t = Type.GetType(yourConfigType); 从配置字符串列表创建类型列表时。
  3. 在配置文件中引用感兴趣的程序集,并以与选项1相同的方式将其加载到DLL中。
  4. 就像在上一个示例中一样,对列表进行硬编码。

如果选择选项1或3,则在调用Assembly.LoadFrom之前,必须检查以确保尚未将程序集加载到内存中。 您可以通过再次检查AppDomain.CurrentDomain.GetAssemblies().Any(x =>your search query)加载的内容来执行此操作。

还要注意,一旦将程序集加载到应用程序域中,就无法在该应用程序域的生命期内卸载它。 如果您不希望这样做,但仍然想动态查找所有类型,则必须创建第二个应用程序域来查找所有类型,并将它们作为字符串/全限定类型名称的数组/列表返回。 然后,您可以卸载此创建的应用程序域。 另外,如下面的注释中@Peter所正确指出的那样,如果采用这种方法,请使用ReflectionOnlyLoadFrom 这样产生的开销要少得多。

AppDomain.GetAssemblies()告诉您已加载的程序集,而不是所引用的程序集。 我无法谈及您问题的“棱镜”方面,我同意这样的意见,即可能有更好的设计方法。 但…

如果您真的想枚举可能AppDomain加载的所有类型,则可以通过枚举现有程序集中的类型来进行近似估算(即,如您在此处所做的那样,使用AppDomain.CurrentDomain.GetAssemblies() ,但是对于每个程序集,请调用GetReferencedAssemblies() ,它返回一个AssemblyName值数组,可用于加载其他程序集。 对于每种类型,您可以依次检查它们的所有类型(以查找IUserControl的实现者)并调用GetReferencedAssemblies()继续进行递归搜索。

请注意,这仍然不一定会返回您的进程可能加载的IUserControl接口的所有实现。 可以通过在AppDomain的程序集中引用之外的其他方式来加载程序集,例如,通过代码在候选目录中搜索代码,甚至是用户明确命名要加载的程序集。 这就是为什么使用所使用的任何API直接支持的机制是一种更好的方法,以确保您准确找到该API将找到的那些程序集。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM