2012-10-10 14 views
10

Quando elenco tutti i tipi nell'AppDomain corrente, vedo i miei tipi generici con i segnaposti generici. Tuttavia, se istanzio il mio tipo generico con un tipo e poi elencho tutti i tipi nell'appDomain, non vedo i tipi chiusi appena creati.Elenca i tipi chiusi che il runtime ha creato da tipi generici aperti

Nell'esempio sottostante, l'uscita è unica:

Foo`1[T] 

Sto cercando tipo chiuso:

Foo`1[System.Int32] 

C'è un modo per vedere i tipi di chiuse che il runtime ha creato per me in base ai miei tipi generici aperti?

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

Ho anche provato a cercare tutti i tipi con l'argomento generico nella speranza di scoprire il tipo chiuso.

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

Questo è solo per soddisfare la mia curiosità.

+0

Se ho capito bene, questo riflesso otterrà semplicemente i tipi definiti nei metadati, che contiene nel tuo caso solo la definizione del tipo generico. Poiché i tipi specifici possono essere costruiti dai tipi generici dinamicamente in fase di esecuzione (sempre usando il reflection, passando gli argomenti generici) - puoi vedere che non c'è modo di metterli nei metadati ... quindi, qualche altro meccanismo (non esplorazione dei metadati) dovrebbe essere usato per trovare i tipi specifici che sono stati creati. –

+0

Vedo. Questo spiega perché non riesco a vedere i tipi creati in fase di runtime, non sono nei metadati che il reflection sta interrogando. Mi chiedo quale sarebbe l'altro meccanismo? –

+1

C'è un tipo privato dentro mscorlib chiamato TypeNameParser che ha un metodo GetNames che restituisce una matrice di stringhe, ma quando sto provando a usarlo sotto riflessione sto ricevendo errori fatali che mi ricordano quanto poco so di oggetti COM e interop, e che in genere non dovrei scherzare con i tipi privati ​​all'interno di mscorlib :-P Comunque sto ancora cercando una soluzione elegante. –

risposta

5

Per quanto posso capire in questo caso Foo<T> è un tipo generico non associato aperto, quindi in fase di esecuzione CLR la utilizzerà come modello/scheletro di costruire e chiudere un tipo generico con il tipo di parametro tipo specificato (Foo<int> , Foo<object>, ecc.). Quindi, in pratica, Foo<int> è un'implementazione runtime dello scheletro Foo<T>.

Ora, a tempo di esecuzione è possibile ottenere il tipo di Foo<int> sia utilizzando typeof(Foo<int>) o typeof(Foo<>).MakeGenericType(new[] { typeof(int) }) e non è lo stesso Type e non avrebbe senso per essere. Ma guarda più vicino e vedrai che sia typeof(Foo<T>) e typeof(Foo<int>) condividono lo stesso token di metadati e GUID.

Un'altra cosa interessante è che typeof(Foo<int>).Assembly sarà quello che ti aspetteresti, ma come hai già notato non puoi ottenere quel tipo dall'Assemblea.

Questo perché Foo<int> non è definito nell'assembly (è possibile controllare i metadati dell'assieme con Reflector/ILSpy). In fase di esecuzione il CLR creerà ("costruisci") una versione specializzata ("chiusa") di Foo<T> per Foo<int> (tipo chiuso così costruito di una definizione di tipo generico aperto illimitato) e "assegnagli" un Type. Quindi, a meno che il CLR non esponga direttamente in qualche modo l'elenco di tipi generici chiusi che genera in fase di esecuzione, sei sfortunato.

Anche qui è un frammento che potrebbe confermare quello che sto dicendo:

Anche se ogni costruzione di un tipo generico, come ad esempio il nodo < Modulo> e Nodo < String>, ha un proprio tipo distinto identità, il CLR è in grado di riutilizzare gran parte del codice compilato JIT effettivo tra le istanze di tipo . Ciò riduce drasticamente il codice bloat ed è possibile perché le varie istanze di un tipo generico vengono espanse al tempo di esecuzione . Tutto ciò che esiste di un tipo costruito in fase di compilazione è un riferimento di tipo . Quando gli assiemi A e B fanno entrambi riferimento a un tipo generico definito in un terzo assieme, i tipi costruiti vengono espansi al tempo di esecuzione .Ciò significa che, oltre alla condivisione delle identità di tipo CLR (se appropriato), digitare le istanze dagli assiemi A e B anche condividere le risorse di runtime come codice nativo e metadati espansi.

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

+2

Questo è un po 'di "magia CLR run-time": il costruttore statico di Foo viene chiamato per ogni tipo di "caricamento" chiuso :-) P.S. Dov'è Skeet quando hai bisogno di lui? –

1

risposta di Ivan è in gran parte a destra, ma sostenendo che i metadati di montaggio non contiene alcuna informazione sui tipi di costruzione non è del tutto corretto. Tutti i tipi costruttivi sono definiti in assembly che li utilizza e strumenti come Mono.Cecil consentono di vederlo. I tipi costruttivi non sono esposti attraverso il riflesso e persino Mono.Cecil rende piuttosto difficile individuarli tutti.

Fondamentalmente è necessario esaminare tutti i tipi che sono utilizzati nell'assieme, ad es. tipi di proprietà, tipi di ritorno, tipi di variabili locali, ecc. Queste informazioni sono contenute nei metadati di assembly e ragionevolmente facili da enumerare con Mono.Cecil. Quindi basta applicare un semplice filtro che rileva se il tipo è costruito. Si noti che potrebbe essere necessario esaminare vari assembly che fanno riferimento alla definizione di tipo generico per trovare tutti i tipi costruiti da esso.

Esistono due limiti a questa soluzione. Innanzitutto, i tipi costruiti tramite riflessione non appaiono in alcun assemblaggio. In secondo luogo, alcuni tipi costruiti sono incorporati in tipi/metodi generici e i loro argomenti di tipo generico sono noti solo dopo l'istanza del loro tipo/metodo genitore con particolari argomenti di tipo generico.