2012-10-10 55 views
10

当我列出当前AppDomain中的所有类型时,我会看到泛型类型与泛型占位符。但是,如果我用一个类型实例化我的泛型类型,然后列出appDomain中的所有类型,我没有看到新创建的闭合类型。列出运行时从开放泛型类型创建的已关闭类型

在下面的例子中,产量只有:

Foo`1[T] 

我在寻找使密闭型:

Foo`1[System.Int32] 

有没有办法看到封闭类型的运行时有基于我的开放泛型类型为我创建?

class Foo<T> 
{ 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var tmp = new Foo<int>(); 
     ListTypes(); 
    } 

    private static void ListTypes() 
    { 
     var types = from assembly in AppDomain.CurrentDomain.GetAssemblies() 
         from type in assembly.GetTypes() 
         where type.Name.Contains("Foo") 
         select type; 

     foreach (var type in types) 
      Console.WriteLine(type.ToString()); 
    } 
} 

我也试图找到所有类型的泛型参数,希望发现封闭类型。

class Foo<T> 
{ 
} 

class Bar 
{ 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var tmp = new Foo<Bar>(); 
     ListTypes(); 
    } 

    private static void ListTypes() 
    { 
     var types = from assembly in AppDomain.CurrentDomain.GetAssemblies() 
         from type in assembly.GetTypes() 
         where type.IsGenericType 
         && type.GetGenericArguments().Contains(typeof(Bar)) 
         select type; 

     foreach (var type in types) 
      Console.WriteLine(type.ToString()); 
    } 
} 

这只是为了满足我的好奇心。

+0

如果我理解正确,这个反射将简单地获取在元数据中定义的类型,在您的情况下只包含泛型类型定义。由于特定类型可以在运行时从泛型类型动态构建(再次使用反射,传递泛型参数) - 您可以看到无法将它们放入元数据中......所以,其他一些机制(不是元数据探索)必须用于查找创建的特定类型。 –

+0

我明白了。这就解释了为什么我看不到在运行时创建的类型,它们不在反射查询的元数据中。我想知道其他机制会是什么? –

+1

在mscorlib中有一个名为TypeNameParser的私有类型,它有一个返回字符串数组的GetNames方法,但是当我尝试在反射下使用它时,我收到致命错误,提醒我对COM对象和互操作知之甚少,并且通常我不应该在mscorlib中使用私有类型:-P尽管如此,仍然在寻找一个优雅的解决方案。 –

回答

5

至于我能在这种情况下Foo<T>明白的是一个开放的未绑定泛型类型,所以在运行时,CLR将使用它作为蓝本/骨架构造和关闭泛型类型指定类型参数类型(Foo<int>Foo<object>等)。所以基本上Foo<int>是一个运行时构建的Foo<T>骨架的实现。

现在,在运行时,你可以通过使用typeof(Foo<int>)typeof(Foo<>).MakeGenericType(new[] { typeof(int) })得到Foo<int>类型,它是不一样的Type,它是没有意义的它是。但仔细观察,您会看到typeof(Foo<T>)typeof(Foo<int>)共享相同的元数据标记和GUID。

另一个有趣的事情是,typeof(Foo<int>).Assembly将是你所期望的,但正如你已经注意到,你不能从大会得到这种类型。

这是因为Foo<int>未在程序集中定义(您可以使用Reflector/ILSpy检查程序集元数据)。在运行时,CLR将为Foo<int>(如此构建的无限开放泛型类型定义的封闭类型)创建(“构建”)Foo<T>的专用(“封闭”)版本,并“给它”一个Type。因此,除非CLR直接以某种方式公开它在运行时生成的封闭泛型类型列表,否则您运气不佳。

而且,这里是一个可能证实我所说的一个片段:

即使泛型类型,如节点<表> 和节点<字符串>的每个建筑,都有其独特的类型CLR可以在 实例之间重用大量实际的JIT编译代码。这极大地减少了代码膨胀并且可能是 ,因为泛型的各种实例在 运行时被扩展。所有在编译时存在的构造类型都是 类型的引用。当组件A和B都引用在第三个程序集中定义的通用类型 时,它们的构造类型在运行时扩展为 。这意味着除了共享CLR类型标识 (适当时)之外,还可以从程序集A和B中输入实例,并共享 共享运行时资源,例如本地代码和扩展元数据。

http://msdn.microsoft.com/en-us/magazine/cc163683.aspx

+2

这是一些沉重的“运行时CLR魔术”:为每个封闭类型“加载”调用Foo 的静态构造函数:-) P.S.当你需要他时,Skeet在哪里? –

1

伊万的回答大多是对的,但声称集元数据不包含构造类型的任何信息是不太正确的。所有构造的类型在使用它们的程序集中定义,并使用像Mono.Cecil这样的工具让您看到它。构造类型不会通过反射暴露,甚至Mono.Cecil也很难找到它们。

基本上你必须通过所有类型使用在装配,例如,属性类型,返回类型,局部变量类型等。这些信息包含在程序集元数据中,并且可以用Mono.Cecil进行合理的枚举。然后应用简单的过滤器来检测类型是否构造。请注意,您可能必须遍历几个引用泛型类型定义的程序集,以便查找从中构建的所有类型。

该解决方案有两个限制。首先,通过反射构建的类型自然不会出现在任何程序集中。其次,一些构造类型嵌入到泛型类型/方法中,并且它们的泛型类型参数只有在它们的父类型/方法使用特定泛型类型参数实例化后才知道。

相关问题