Il tipo nullable non è un tipo nullable?

Stavo facendo alcuni test con i tipi nullable, e non ha funzionato abbastanza come mi aspettavo:

int? testInt = 0; Type nullableType = typeof(int?); Assert.AreEqual(nullableType, testInt.GetType()); // not the same type 

Questo non funziona neanche:

 DateTime? test = new DateTime(434523452345); Assert.IsTrue(test.GetType() == typeof(Nullable)); //FAIL DateTime? test = new DateTime(434523452345); Assert.IsTrue(test.GetType() == typeof(Nullable)); //STILL FAIL 

La mia domanda è: perché testInt.GetType () restituisce int e typeof (int?) Restituisce il vero tipo nullable?

Secondo il MSDN :

Chiamando GetType su un tipo Nullable si esegue un’operazione di boxing quando il tipo viene convertito implicitamente in Object. Pertanto GetType restituisce sempre un object Type che rappresenta il tipo sottostante, non il tipo Nullable.

Quando box un object nullable, solo il tipo sottostante è in scatola.

Di nuovo, da MSDN :

La boxing di un tipo di valore nullable non nullo contiene il tipo di valore stesso, non il System.Nullable che racchiude il tipo di valore.

Oltre alla risposta corretta di Romain, se vuoi confrontare i tipi “reali” (cioè, senza convertire implicitamente qualsiasi tipo annullabile nel suo tipo sottostante), puoi creare un metodo di estensione in questo modo:

 public static class MyExtensionMethods { public static Type GetRealType(this T source) { return typeof(T); } } 

E poi prova i seguenti test:

 int? a = 0; Console.WriteLine(a.GetRealType() == typeof(int?)); // True Console.WriteLine(a.GetRealType() == typeof(int)); // False int b = 0; Console.WriteLine(b.GetRealType() == typeof(int)); // True Console.WriteLine(b.GetRealType() == typeof(int?)); // False DateTime? c = DateTime.Now; Console.WriteLine(c.GetRealType() == typeof(DateTime?)); // True Console.WriteLine(c.GetRealType() == typeof(DateTime)); // False DateTime d = DateTime.Now; Console.WriteLine(d.GetRealType() == typeof(DateTime)); // True Console.WriteLine(d.GetRealType() == typeof(DateTime?)); // False 

MODIFICARE…

Per completezza – e richiesto dai commenti di SLaks qui sotto – ecco una versione alternativa che usa solo il tipo in fase di compilazione quando source è null o Nullable<> ; altrimenti usa GetType e restituisce il tipo di runtime:

 public static class MyExtensionMethods { public static Type GetRealType(this T source) { Type t = typeof(T); if ((source == null) || (Nullable.GetUnderlyingType(t) != null)) return t; return source.GetType(); } } 

Sebbene C # pretenda che le posizioni di archiviazione di tipo value contengano istanze di tipi derivati ​​da System.ValueType , che a sua volta deriva da System.Object , non è proprio vero. Ogni tipo derivato da System.ValueType rappresenta in realtà due tipi di cose molto diversi:

  1. Una raccolta di byte che (per i tipi primitivi) rappresenta direttamente i dati o (per tipi di struttura non primitivi) contiene i contenuti di tutti i campi, pubblici e privati, ma non contiene alcuna informazione di tipo.
  2. Un object heap standalone, che contiene un’intestazione di object in aggiunta a quella precedente, il cui tipo è derivato da “System.ValueType”.

Le posizioni di memoria di un tipo di valore contengono il primo; gli oggetti heap di un tipo di valore contengono il secondo.

Per vari motivi, Microsoft ha deciso che Nullable dovrebbe supportare solo il primo utilizzo. Se si tenta di passare un percorso di archiviazione di tipo Nullable al codice che si aspetta un riferimento a un object heap, il sistema convertirà l’elemento in T se HasValue è true, oppure semplicemente passerà un riferimento null se HasValue è falso . Sebbene esistano modi per creare un object heap di tipo Nullable , i normali metodi di conversione di un percorso di memorizzazione di tipo valore in un object heap non generano mai uno.

Si noti inoltre che la chiamata a GetType() in un percorso di memorizzazione del valore non valuterà il tipo di posizione di archiviazione, ma convertirà invece il contenuto di tale posizione di archiviazione in un object heap e quindi restituirà il tipo dell’object risultante. Poiché le posizioni di memoria di tipo Nullable vengono convertite in istanze di oggetti di T o null, nulla in un’istanza di object dirà se il percorso di archiviazione da cui proviene era un Nullable .

Un modo semplice per verificare che sta utilizzando l’operatore “è”:

 (i is Nullable) || (i is Nullable) || (i is Nullable) || (i is Nullable) 

Ho capito di aver letto queste due pagine MSDN:

http://msdn.microsoft.com/en-us/library/ms366789(v=vs.90).aspx

http://msdn.microsoft.com/en-us/library/ms228597%28v=VS.90%29.aspx

Saluti!