Aquí hay dos problemas: 1) probar para ver si un Tipo es anulable; y 2) pruebas para ver si un objeto representa un tipo que admite valores NULL.
Para el problema 1 (probar un tipo), aquí hay una solución que he usado en mis propios sistemas: solución TypeIsNullable-check
Para el problema 2 (probar un objeto), la solución anterior de Dean Chalk funciona para los tipos de valor, pero no funciona para los tipos de referencia, ya que el uso de la sobrecarga <T> siempre devuelve falso. Dado que los tipos de referencia son inherentemente anulables, probar un tipo de referencia siempre debe devolver verdadero. Consulte la nota [Acerca de "nulabilidad"] a continuación para obtener una explicación de estas semánticas. Por lo tanto, aquí está mi modificación al enfoque de Dean:
public static bool IsObjectNullable<T>(T obj)
{
// If the parameter-Type is a reference type, or if the parameter is null, then the object is always nullable
if (!typeof(T).IsValueType || obj == null)
return true;
// Since the object passed is a ValueType, and it is not null, it cannot be a nullable object
return false;
}
public static bool IsObjectNullable<T>(T? obj) where T : struct
{
// Always return true, since the object-type passed is guaranteed by the compiler to always be nullable
return true;
}
Y aquí está mi modificación al código de prueba del cliente para la solución anterior:
int a = 123;
int? b = null;
object c = new object();
object d = null;
int? e = 456;
var f = (int?)789;
string g = "something";
bool isnullable = IsObjectNullable(a); // false
isnullable = IsObjectNullable(b); // true
isnullable = IsObjectNullable(c); // true
isnullable = IsObjectNullable(d); // true
isnullable = IsObjectNullable(e); // true
isnullable = IsObjectNullable(f); // true
isnullable = IsObjectNullable(g); // true
La razón por la que modifiqué el enfoque de Dean en IsObjectNullable <T> (T t) es que su enfoque original siempre devolvió falso para un tipo de referencia. Dado que un método como IsObjectNullable debería ser capaz de manejar valores de tipo de referencia y dado que todos los tipos de referencia son inherentemente anulables, entonces si se pasa un tipo de referencia o un valor nulo, el método siempre debe devolver verdadero.
Los dos métodos anteriores podrían reemplazarse con el siguiente método único y lograr el mismo resultado:
public static bool IsObjectNullable<T>(T obj)
{
Type argType = typeof(T);
if (!argType.IsValueType || obj == null)
return true;
return argType.IsGenericType && argType.GetGenericTypeDefinition() == typeof(Nullable<>);
}
Sin embargo, el problema con este último enfoque de método único es que el rendimiento sufre cuando se usa un parámetro Nullable <T>. Se necesita mucho más tiempo de procesador para ejecutar la última línea de este método único que para permitir que el compilador elija la segunda sobrecarga del método que se muestra anteriormente cuando se usa un parámetro de tipo Nullable <T> en la llamada IsObjectNullable. Por lo tanto, la solución óptima es utilizar el enfoque de dos métodos ilustrado aquí.
PRUEBA: Este método funciona de manera confiable solo si se llama usando la referencia de objeto original o una copia exacta, como se muestra en los ejemplos. Sin embargo, si un objeto anulable se encuadra en otro Tipo (como un objeto, etc.) en lugar de permanecer en su forma original Anulable <>, este método no funcionará de manera confiable. Si el código que llama a este método no utiliza la referencia de objeto original sin caja o una copia exacta, no puede determinar de manera confiable la nulabilidad del objeto utilizando este método.
En la mayoría de los escenarios de codificación, para determinar la nulabilidad, uno debe confiar en probar el Tipo del objeto original, no su referencia (por ejemplo, el código debe tener acceso al Tipo original del objeto para determinar la nulabilidad). En estos casos más comunes, IsTypeNullable (ver enlace) es un método confiable para determinar la nulabilidad.
PD: sobre "nulabilidad"
Debo repetir una declaración sobre la nulabilidad que hice en una publicación separada, que se aplica directamente a abordar este tema correctamente. Es decir, creo que el foco de la discusión aquí no debería ser cómo verificar si un objeto es un tipo Nullable genérico, sino más bien si se puede asignar un valor nulo a un objeto de su tipo. En otras palabras, creo que deberíamos determinar si un tipo de objeto es anulable, no si es anulable. La diferencia está en la semántica, es decir, las razones prácticas para determinar la nulabilidad, que generalmente es lo único que importa.
En un sistema que utiliza objetos con tipos posiblemente desconocidos hasta el tiempo de ejecución (servicios web, llamadas remotas, bases de datos, feeds, etc.), un requisito común es determinar si se puede asignar un valor nulo al objeto o si el objeto puede contener un nulo Realizar tales operaciones en tipos no anulables probablemente producirá errores, generalmente excepciones, que son muy costosos tanto en términos de rendimiento como de requisitos de codificación. Para adoptar el enfoque altamente preferido de evitar proactivamente tales problemas, es necesario determinar si un objeto de un Tipo arbitrario es capaz de contener un valor nulo; es decir, si es generalmente 'anulable'.
En un sentido muy práctico y típico, la nulabilidad en términos .NET no implica necesariamente que el Tipo de un objeto sea una forma de Nullable. De hecho, en muchos casos, los objetos tienen tipos de referencia, pueden contener un valor nulo y, por lo tanto, son anulables; ninguno de estos tiene un tipo Nullable. Por lo tanto, para fines prácticos en la mayoría de los escenarios, las pruebas deben realizarse para el concepto general de nulabilidad, en comparación con el concepto de Nullable dependiente de la implementación. Por lo tanto, no debemos colgarnos enfocándonos únicamente en el tipo .NET Nullable, sino más bien incorporar nuestra comprensión de sus requisitos y comportamiento en el proceso de enfocarnos en el concepto general y práctico de nulabilidad.