Tipo anulable no es un tipo que admite nulo

Estaba haciendo algunas pruebas con tipos anulables, y no funcionó del todo como esperaba:

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

Esto tampoco funciona:

 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 

Mi pregunta es por qué testInt.GetType () devuelve int, y typeof (int?) Devuelve el verdadero tipo que admite nulos?

De acuerdo con MSDN :

Llamar a GetType en un tipo Nullable provoca que se realice una operación de boxing cuando el tipo se convierte implícitamente en Object. Por lo tanto, GetType siempre devuelve un objeto Type que representa el tipo subyacente, no el tipo Nullable.

Cuando coloca un objeto que admite nulos, solo el tipo subyacente está enmarcado.

Nuevamente, desde MSDN :

El boxeo de un tipo de valor que no admite nulos delimita el tipo de valor en sí, no el System.Nullable que ajusta el tipo de valor.

Además de la respuesta correcta de Romain, si desea comparar los tipos “reales” (es decir, sin convertir implícitamente ningún tipo que admite nulos a su tipo subyacente), entonces puede crear un método de extensión como ese:

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

Y luego prueba las siguientes pruebas:

 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 

EDITAR…

Para completarlo, y como lo sugieren los comentarios de SLaks a continuación, aquí hay una versión alternativa que solo usa el tipo de tiempo de comstackción cuando la source es null o Nullable<> ; de lo contrario, usa GetType y devuelve el tipo de tiempo de ejecución:

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

Aunque C # simula que las ubicaciones de almacenamiento de tipo valor contienen instancias de tipos derivados de System.ValueType , que a su vez deriva de System.Object , eso no es realmente cierto. Cada tipo derivado de System.ValueType realidad representa dos tipos muy diferentes de cosas:

  1. Una colección de bytes que (para tipos primitivos) representa los datos directamente, o (para tipos de estructura no primitivos) contiene los contenidos de todos los campos, públicos y privados, pero no contiene ningún tipo de información.
  2. Un objeto de montón autónomo, que contiene un encabezado de objeto además del anterior, cuyo tipo se deriva de `System.ValueType`.

Las ubicaciones de almacenamiento de un tipo de valor tienen el primero; los objetos de montón de un tipo de valor tienen el segundo.

Por varias razones, Microsoft decidió que Nullable solo debería admitir el primer uso. Si uno intenta pasar una ubicación de almacenamiento de tipo Nullable al código que espera una referencia a un objeto Heap, el sistema convertirá el elemento a T si HasValue es verdadero, o simplemente pasará una referencia nula si HasValue es falso . Si bien hay formas de crear un objeto de montón de tipo Nullable , los métodos normales para convertir una ubicación de almacenamiento de tipo de valor en un objeto de montón nunca generarán uno.

Tenga en cuenta también que llamar a GetType() en una ubicación de almacenamiento de valor no evaluará realmente el tipo de ubicación de almacenamiento, sino que convertirá los contenidos de esa ubicación de almacenamiento en un objeto de montón y luego devolverá el tipo del objeto resultante. Debido a que las ubicaciones de almacenamiento de tipo Nullable se convierten a instancias de objeto de T o a nulo, nada en una instancia de objeto indicará si la ubicación de almacenamiento de la que procede es una Nullable .

Una forma sencilla de comprobar que está utilizando el operador “es”:

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

Me di cuenta de que estaba leyendo estas dos páginas de 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

¡Aclamaciones!