搜索程序集中的类型

时间:2012-09-14 10:32:02

标签: c# .net reflection appdomain

我遇到了代码Type.GetType(myTypeName)正在返回null的问题,因为具有该类型的程序集不是当前正在执行的程序集。

我为此问题找到的解决方案是:

var assemblies = AppDomain.CurrentDomain.GetAssemblies();
Type myType = assemblies.SelectMany(a => a.GetTypes())
                        .Single(t => t.FullName == myTypeName);

问题是第一次运行此代码会导致异常"Sequence contains no matching element"。当我再次调用这部分代码时 - 一切正常,需要输入Type。

任何人都可以解释这种行为吗?为什么在第一次调用的范围内没有找到所需的汇编/类型?

5 个答案:

答案 0 :(得分:3)

您遇到的问题是由AppDomainGetAssemblies方法的设计引起的 - 根据此方法的文档:

  

获取已加载到此应用程序域的执行上下文中的程序集。

因此,当你的程序类型第一次找不到时 - 它的程序集显然没有被应用程序加载。然后 - 当使用包含相关类型的程序集的某些功能时 - 已加载程序集,并且相同的代码已经可以找到缺少的类型。

请尝试直接加载程序集。而不是使用:

var assemblies = AppDomain.CurrentDomain.GetAssemblies();

你可以使用:

List<Assembly> assemblies = Assembly.GetEntryAssembly().GetReferencedAssemblies().Select(assembly => Assembly.LoadFrom(assembly.Name)).ToList();

答案 1 :(得分:2)

类型可能在尚未加载的程序集中。然后在你的程序中,它就是。如果你查看输出窗口,这应该让你知道何时加载程序集。

答案 2 :(得分:1)

另一个answer显示了获取Type中可能未加载的Assembly(在运行时)的最佳方式:

var T1 = Type.GetType("System.Web.Configuration.IAssemblyCache, " + 
                      "System.Web, " + 
                      "Version=4.0.0.0, " + 
                      "Culture=neutral, " + 
                      "PublicKeyToken=b03f5f7f11d50a3a");

正如您所看到的,遗憾的是,该方法要求您提供Type的完整AssemblyQualifiedName,并且无法使用abbreviated forms的任何assembly name我试过了。这在某种程度上违背了我们的主要目的。如果您知道关于装配的更多细节,那么自己加载它就不会那么困难了:

var T2 = Assembly.Load("System.Web, " + 
                       "Version=4.0.0.0, " + 
                       "Culture=neutral, " + 
                       "PublicKeyToken=b03f5f7f11d50a3a")
                 .GetType("System.Web.Configuration.IAssemblyCache");

尽管这两个示例看起来很相似,但它们执行的代码路径却截然不同;请注意,例如,后一版本在Assembly.GetType上调用实例重载而不是静态调用Type.GetType。因此,第二个版本可能更快或更有效。无论哪种方式,你似乎最终都在下面的CLR内部方法,并且第二个参数设置为false,这就是为什么 这两个方法都没有遇到搜索的麻烦代表你所需的集会。

  

[System.Runtime.Remoting.RemotingServices]
  private static RuntimeType LoadClrTypeWithPartialBindFallback
  字符串类型名称,
  bool partialFallback);

从这些不便中向前迈出的一小步将是自己调用此CLR方法,但将partialFallback参数设置为 true 。在此模式下,该函数将接受AssemblyQualifiedName的截断版本,并在必要时定位并加载相关程序集:

static Func<String, bool, TypeInfo> LoadClrTypeWithPartialBindFallback =
    typeof(RemotingServices)
    .GetMethod("LoadClrTypeWithPartialBindFallback", (BindingFlags)0x28)
    .CreateDelegate(typeof(Func<String, bool, TypeInfo>))
    as Func<String, bool, TypeInfo>;

// ...

var T3 = LoadClrTypeWithPartialBindFallback(
            "System.Web.Configuration.IAssemblyCache, System.Web",
            true);    //  <-- enables searching for the assembly

这如图所示,并且还继续支持指定完整的AssemblyQualifiedName,如前面的示例所示。这是一个很小的改进,但它仍然不是完全不合格的命名空间搜索,因为你仍然必须指定程序集的短名称,即使它可能是从类型名称本身中出现的命名空间推断出来的。

答案 3 :(得分:0)

如果排除mscorlib程序集? ,你可以试试这个:

  var assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(asb=>!asb.FullName.StartsWith("mscorlib")).ToList();

Type myType = assemblies.SelectMany(a => a.GetTypes())
                        .Single(t => t.FullName == myTypeName);

答案 4 :(得分:0)

顺便说一句,如果你知道包含该类型的程序集的FullName(或者包含TypeForwardedToAttribute类型的程序集)可以使用Type.GetType。具体来说,Type.GetType(Assembly.CreateQualifiedName(assembly.FullName, myTypeName))看起来像是:

Type.GetType("Some.Complete.Namespace.myTypeName, Some.Assembly.Name, Version=1.2.3.4, Culture=neutral, PublicKeyToken=ffffffffffffffff");

假设框架可以解析程序集的位置,那么将加载程序集(如果尚未加载)。

这是我的示例LinqPad查询确认了有用的TypeForwardedToAttribute评论:

var u = (from a in AppDomain.CurrentDomain.GetAssemblies()
         let t = a.GetType("System.Lazy`2")
         where t != null
         select t).FirstOrDefault();
(u?.AssemblyQualifiedName).Dump();
u = Type.GetType("System.Lazy`2, System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
(u?.AssemblyQualifiedName).Dump();

输出:

  


  System.Lazy`2,System.ComponentModel.Composition,Version = 4.0.0.0,Culture = neutral,PublicKeyToken = b77a5c561934e089