2013-03-26 43 views
6

Ho incontrato lo strano comportamento del generico. Di seguito è riportato il codice che utilizzo per il test.Comportamento strano generico

public static class Program 
{ 
    public static void Main() 
    { 
     Type listClassType = typeof(List<int>).GetGenericTypeDefinition(); 
     Type listInterfaceType = listClassType.GetInterfaces()[0]; 

     Console.WriteLine(listClassType.GetGenericArguments()[0].DeclaringType); 
     Console.WriteLine(listInterfaceType.GetGenericArguments()[0].DeclaringType); 
    } 
} 

uscita:

System.Collections.Generic.List`1[T] 
System.Collections.Generic.List`1[T] 

ho trovato molto strano che la seconda chiamata Console.WriteLine viene visualizzata una classe, non un'interfaccia, perché io uso una definizione di tipo generico. È corretto questo comportamento?

Sto cercando di implementare inferenze di tipo generico nel mio compilatore. Supponiamo che io abbia il codice qui sotto.

public static class GenericClass 
{ 
    public static void GenericMethod<TMethodParam>(IList<TMethodParam> list) { } 
} 

e voglio chiamare questo metodo come segue:

GenericClass.GenericMethod(new List<int>()); 

Al fine di verificare la possibilità di inferenza, devo confrontare il tipo nella firma del metodo, e il tipo di argomenti passati. Ma il codice sottostante restituisce false.

typeof(GenericClass).GetMethods()[0].GetParameters()[0].ParameterType == listInterfaceType; 

importante usare sempre Type.GetGenericTypeDefinition per questi confronti?

+0

Beh, in ogni caso, che si riforniscono la catena chiamata dalla lista generica non legato , in modo che il tipo di dichiarazione che sarà sempre di tipo generico non legato. Cosa stai cercando di fare? – JerKimball

risposta

15

Stai confondendo due tipi differenti che sono entrambe di nome T. Pensateci come questo:

interface IFoo<TIFOO> { } 
class Foo<TFOO> : IFoo<TFOO> {} 

OK, qual è la definizione tipo generico di Foo<int>? Quello è Foo<TFOO>.

Che cos'è l'interfaccia implementata da Foo<TFOO>? Quello è IFoo<TFOO>.

Che cos'è l'argomento tipo di Foo<TFOO>? Ovviamente TFOO.

Che tipo ha dichiaratoTFOO? Foo<TFOO> dichiarato.

Che cos'è l'argomento tipo di IFoo<TFOO>? Ovviamente TFOO, nonTIFOO.

Che tipo ha dichiaratoTFOO? Foo<TFOO> dichiarato. nonIFoo<TFOO>. TFOO viene da Foo.

Ha senso?

+0

Non seguo il passaggio da "Che tipo * ha dichiarato * TFOO" al motivo per cui il tipo di interfaccia viene stampato come 'System.Collections.Generic.List'1 [T]'. nell'esempio MSDN, i nomi delle * interfacce * sono stampati con * tipi di calcestruzzo *. Non capisco perché il problema di chi dichiara 'TFOO' faccia stampare il nome di una classe piuttosto che il nome di un'interfaccia. http://msdn.microsoft.com/en-us/library/system.type.getinterfaces.aspx –

+0

@EricJ .: Gli esempi nel collegamento non utilizzano '.DeclaringType'. – recursive

+0

@recursive: lo so, ma continuo a non capire il passaggio. Sono abbastanza sicuro che Eric Lippert abbia ragione in quello che dice. –

2

L'aggiunta di una seconda risposta, perché si è aggiunto una seconda domanda:

Sto cercando di implementare inferenza di tipo generico nel mio compilatore ...

E così presumo che si sta utilizzando riflessione per costruire un compilatore. Questa potrebbe non essere una buona idea.La riflessione è molto più performante di quanto non fosse agli inizi, ma è ancora pesante rispetto al lavorare direttamente con i token. E la riflessione emessa non può emettere ogni possibile topologia dei tipi; viene incasinato in alcuni scenari che coinvolgono tipi di strutture annidate.

Vorrei prendere in considerazione l'utilizzo di CCI. Abbiamo usato una versione modificata di CCI per Roslyn.

il codice sottostante restituisce false.

typeof(GenericClass).GetMethods()[0].GetParameters()[0].ParameterType == listInterfaceType 

Questo è corretto. Il tipo di parametro è IList<TMethodParam> e listInterfaceType è IList<T> dove T è il tipo di parametro generico dichiarato da List<T>, non il tipo di parametro generico dichiarato da IList<T>. Questi sono tutti tipi diversi.

importante usare sempre Type.GetGenericTypeDefinition per questi confronti?

Se si desidera vedere se due tipi generici sono entrambe le costruzioni dello stesso tipo generico, sì. Se non è quello che vuoi controllare, allora no.

Questo tipo di sistema è complicato, quindi state molto attenti.

Questo è un altro motivo per andare con un approccio basato su token piuttosto che un approccio basato riflessione di tipo-oggetto. Quando hai i token in mano è molto più facile distinguere tra uno TypeDef e uno TypeRef.