2012-10-10 20 views
10

Cuando enumero todos los tipos en el dominio de aplicación actual, veo mis tipos genéricos con los marcadores de posición genéricos. Sin embargo, si instanciar mis tipos genéricos con un tipo y luego enumerar todos los tipos en el dominio de la aplicación, no veo los tipos cerrados recientemente creados.Enumere los tipos cerrados que el tiempo de ejecución ha creado a partir de los tipos genéricos abiertos

En el ejemplo siguiente, la salida es única:

Foo`1[T] 

Busco tipo cerrado:

Foo`1[System.Int32] 

¿Hay una manera de ver los tipos cerrados que tiene el tiempo de ejecución creado para mí basado en mis tipos genéricos abiertos?

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

También he intentado encontrar todos los tipos por el argumento genérico con la esperanza de descubrir el tipo cerrado.

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

Esto es sólo para satisfacer mi curiosidad.

+0

Si entiendo correctamente, esta reflexión simplemente obtendrá los tipos definidos en los metadatos, que contienen en su caso solo la definición de tipo genérico. Dado que los tipos específicos se pueden construir de los tipos genéricos dinámicamente en tiempo de ejecución (de nuevo usando reflexión, pasando los argumentos genéricos), puede ver que no hay forma de ponerlos en los metadatos ... por lo tanto, algún otro mecanismo (no exploración de metadatos) tendría que usarse para encontrar los tipos específicos que se crearon. –

+0

Ya veo. Eso explica por qué no puedo ver los tipos creados en tiempo de ejecución, no están en los metadatos que la reflexión está consultando. Me pregunto cuál sería el otro mecanismo. –

+1

Hay un tipo privado dentro de mscorlib llamado TypeNameParser que tiene un método GetNames que devuelve una matriz de cadenas, pero cuando intento usarlo bajo reflexión recibo errores fatales que me recuerdan lo poco que sé de los objetos COM y la interoperabilidad, y que, en general, no debería perder el tiempo con tipos privados dentro de mscorlib :-P Todavía estoy buscando una solución elegante. –

Respuesta

5

Por lo que yo puedo entender en este caso Foo<T> es un tipo genérico no unido abierta, así en tiempo de ejecución el CLR va a usar como un modelo/esqueleto para construir y cerrar un tipo genérico con el tipo de parámetro tipo especificado (Foo<int> , Foo<object>, etc.). Así que básicamente Foo<int> es una implementación construida en tiempo de ejecución del esqueleto Foo<T>.

Ahora, en tiempo de ejecución se puede obtener el tipo de Foo<int> ya sea mediante el uso de typeof(Foo<int>) o typeof(Foo<>).MakeGenericType(new[] { typeof(int) }) y no es la misma Type y no tendría sentido que sea. Pero mire más de cerca y verá que tanto typeof(Foo<T>) como typeof(Foo<int>) comparten el mismo token de metadatos y GUID.

Otra cosa interesante es que typeof(Foo<int>).Assembly será lo que esperas, pero como habrás notado ya no puedes obtener ese tipo del ensamblado.

Eso es porque Foo<int> no está definido en el conjunto (puede verificar los metadatos del ensamblado con Reflector/ILSpy). En tiempo de ejecución, el CLR creará ("construirá") una versión especializada ("cerrada") del Foo<T> para Foo<int> (así, tipo cerrado construido de una definición de tipo genérico abierto sin límites) y "le dará" un Type. Entonces, a menos que el CLR exponga directamente de alguna manera la lista de tipos genéricos cerrados que genera en tiempo de ejecución, no tiene suerte.

También aquí es un fragmento que podrían confirmar lo que estoy diciendo:

A pesar de que cada construcción de un tipo genérico, como Nodo < Forma> y el Nodo < String>, tiene su propio tipo distinto identidad, el CLR puede reutilizar la mayor parte del código compilado JIT real entre las instancias del tipo . Esto reduce drásticamente la inflamación del código y es posible porque las diversas instancias de un tipo genérico se expanden a tiempo de ejecución. Todo lo que existe de un tipo construido en el momento de la compilación es una referencia de tipo . Cuando los conjuntos A y B hacen referencia a un tipo genérico definido en un tercer conjunto, sus tipos construidos se expanden a tiempo de ejecución.Esto significa que, además de compartir las identidades de tipo CLR (cuando corresponda), escriba instancias de los ensambles A y B también compartiendo recursos de tiempo de ejecución como código nativo y metadatos expandidos.

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

+2

Ésta es una gran "magia de CLR en tiempo de ejecución": se llama al constructor estático de Foo para cada tipo cerrado "cargando" :-) P.S. ¿Dónde está Skeet cuando lo necesitas? –

1

respuesta de Ivan es sobre todo la derecha, pero la reivindicación de que los metadatos de montaje no contiene ninguna información acerca de los tipos construidos no es del todo correcta. Todos los tipos construidos se definen en el ensamblaje que los usa y herramientas como Mono.Cecil le permiten verlo. Los tipos construidos no se exponen a través de la reflexión e incluso el Mono.Cecil hace que sea bastante difícil localizarlos a todos.

Básicamente debe recorrer todos los tipos que son utilizados en el conjunto, p. tipos de propiedades, tipos de devolución, tipos de variables locales, etc. Esta información está contenida en los metadatos del ensamblado y es razonablemente fácil de enumerar con Mono.Cecil. Luego solo aplique filtro simple que detecta si el tipo está construido. Tenga en cuenta que es posible que tenga que recorrer varios ensambles que hacen referencia a la definición de tipo genérico para encontrar todos los tipos construidos a partir de él.

Existen dos limitaciones a esta solución. En primer lugar, los tipos construidos mediante reflexión naturalmente no aparecen en ningún ensamblaje. En segundo lugar, algunos tipos construidos están incrustados en tipos/métodos genéricos y sus argumentos de tipo genérico son conocidos solo después de que su tipo/método principal se instancia con argumentos particulares de tipo genérico.

Cuestiones relacionadas