c# - romano - que diferencia existe entre nulidad y anulabilidad
Tipo anulable no es un tipo que admite nulo (4)
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?
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<T>(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 compilació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<T>(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:
- 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.
- 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<T>
solo debería admitir el primer uso. Si uno intenta pasar una ubicación de almacenamiento de tipo Nullable<T>
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<T>
, 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<T>
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<T>
.
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.
Una forma sencilla de comprobar que está utilizando el operador "es":
(i is Nullable<int>) || (i is Nullable<long>) || (i is Nullable<float>) || (i is Nullable<short>)
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!