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.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With