Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

List closed types that runtime has created from open generic types

Tags:

.net

clr

generics

When I list all the types in the current AppDomain, I see my generic types with the generic placeholders. However, if I instantiate my generic types with a type and then list all the types in the appDomain, I don't see the newly created closed types.

In the example below, the output is only:

Foo`1[T]

I'm looking for the closed type:

Foo`1[System.Int32]

Is there a way to see the closed types that the runtime has created for me based on my open generic types?

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

I have also tried finding all types by the generic argument in hopes of discovering the closed type.

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

This is just to satisfy my curiosity.

like image 898
Nick VanderPyle Avatar asked Oct 10 '12 20:10

Nick VanderPyle


1 Answers

As far as I can understand in this case Foo<T> is an open unbound generic type, so at runtime the CLR will use it as a blueprint/skeleton to construct and close a generic type with the type parameter type specified (Foo<int>, Foo<object>, etc.). So basically Foo<int> is a runtime constructed implementation of the Foo<T> skeleton.

Now, at run-time you can get the type of Foo<int> either by using typeof(Foo<int>) or typeof(Foo<>).MakeGenericType(new[] { typeof(int) }) and it's not the same Type and it wouldn't make sense for it to be. But look closer and you will see that both typeof(Foo<T>) and typeof(Foo<int>) share the same metadata token and GUID.

Another interesting thing is that typeof(Foo<int>).Assembly will be what you would expect, but as you've noticed already you can't get that type from the Assembly.

That's because Foo<int> is not defined in the assembly (you can check the assembly metadata with Reflector/ILSpy). At run-time the CLR will create ("construct") a specialized ("closed") version of the Foo<T> for Foo<int> (so - constructed closed type of an unbounded open generic type definition) and "give" it a Type. So unless the CLR exposes directly somehow the list of closed generic types it generates at run-time you are out of luck.

Also here is a snippet that might confirm what I am saying:

Even though each construction of a generic type, such as Node< Form > and Node< String >, has its own distinct type identity, the CLR is able to reuse much of the actual JIT-compiled code between the type instantiations. This drastically reduces code bloat and is possible because the various instantiations of a generic type are expanded at run time. All that exists of a constructed type at compile time is a type reference. When assemblies A and B both reference a generic type defined in a third assembly, their constructed types are expanded at run time. This means that, in addition to sharing CLR type-identities (when appropriate), type instantiations from assemblies A and B also share run-time resources such as native code and expanded metadata.

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

like image 182
Ivan Zlatev Avatar answered Oct 02 '22 19:10

Ivan Zlatev