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());
    }
}

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

4

2 回答 2

5

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

现在,在运行时,您可以Foo<int>通过使用typeof(Foo<int>)or获得类型,typeof(Foo<>).MakeGenericType(new[] { typeof(int) })但它并不相同Type,而且它没有意义。但仔细观察,您会发现两者都typeof(Foo<T>)共享typeof(Foo<int>)相同的元数据令牌和 GUID。

另一个有趣的事情是这typeof(Foo<int>).Assembly将是您所期望的,但是正如您已经注意到的那样,您无法从 Assembly 中获得该类型。

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

这里还有一个片段可以证实我在说什么:

尽管每个泛型类型的构造(例如 Node<Form> 和 Node<String>)都有自己独特的类型标识,但 CLR 能够在类型实例化之间重用大部分实际的 JIT 编译代码。这极大地减少了代码膨胀并且是可能的,因为泛型类型的各种实例在运行时被扩展。在编译时存在的所有构造类型都是类型引用。当程序集 A 和 B 都引用在第三个程序集中定义的泛型类型时,它们的构造类型会在运行时扩展。这意味着,除了共享 CLR 类型标识(在适当的情况下)之外,来自程序集 A 和 B 的类型实例化还共享运行时资源,例如本机代码和扩展元数据。

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

于 2012-10-10T21:58:16.577 回答
1

Ivan 的回答大部分是正确的,但声称程序集元数据不包含有关构造类型的任何信息并不完全正确。所有构造类型都在使用它们的程序集中定义,并且Mono.Cecil等工具让您看到它。构造类型不会通过反射暴露出来,甚至 Mono.Cecil 也很难找到它们。

基本上,您必须遍历程序集中使用的所有类型,例如属性类型、返回类型、局部变量类型等。这些信息包含在程序集元数据中,并且使用 Mono.Cecil 相当容易枚举。然后只需应用简单的过滤器来检测类型是否被构造。请注意,您可能必须遍历几个引用泛型类型定义的程序集才能找到从它构造的所有类型。

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

于 2013-05-15T15:23:23.843 回答