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

时间:2016-02-15 20:08:41

标签: c# .net wpf reflection prism

我正在使用WPF和Prism开发模块化应用程序 我的所有 UserControls 都有单独的程序集并实现 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)},
      ...
};

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

供参考:

  • 所有程序集都以相同的.NET框架版本为目标。
  • 我的Prism版本是6.1.0
  • 我将使用 userControlTypes 将应用程序内的所有UserControl类型显示给最终用户。
  • IUserControl 界面不包含任何内容。

2 个答案:

答案 0 :(得分:1)

此行为是设计使然。除非调用/输入强制它加载,否则.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. 只需像上一个例子中那样对列表进行硬编码。
  5. 如果选择选项1或3,则必须先检查以确保在调用Assembly.LoadFrom之前尚未将程序集加载到内存中。您可以通过再次检查已加载AppDomain.CurrentDomain.GetAssemblies().Any(x =>your search query)的内容来执行此操作。

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

答案 1 :(得分:1)

AppDomain.GetAssemblies()告诉您加载的程序集,而不是引用的程序集。我无法谈论你的问题的棱镜方面,我同意这些评论,可能有更好的方法来设计它。但...

如果你真的想枚举可能加载到AppDomain中的所有类型,你可以通过枚举现有程序集中的类型(即你已经在这里完成,使用AppDomain.CurrentDomain.GetAssemblies(),然后为每个程序集调用GetReferencedAssemblies()),它返回一组AssemblyName值,您可以使用它们来加载其他程序集。对于其中的每一个,您可以依次检查所有类型(以查找IUserControl的实现者)并调用GetReferencedAssemblies()以继续递归搜索。

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