¿Cómo comprobar si un objeto es anulable?
Pregunta
¿Cómo puedo comprobar si un objeto dado es anulable en otras palabras, la forma de aplicar el siguiente método ...
bool IsNullableValueType(object o)
{
...
}
EDIT: Busco tipos de valores con valores nulos. No he tenido en cuenta los tipos de ref.
//Note: This is just a sample. The code has been simplified
//to fit in a post.
public class BoolContainer
{
bool? myBool = true;
}
var bc = new BoolContainer();
const BindingFlags bindingFlags = BindingFlags.Public
| BindingFlags.NonPublic
| BindingFlags.Instance
;
object obj;
object o = (object)bc;
foreach (var fieldInfo in o.GetType().GetFields(bindingFlags))
{
obj = (object)fieldInfo.GetValue(o);
}
OBJ ahora se refiere a un objeto de tipo bool
(System.Boolean
) con valor igual a true
. Lo que realmente quería era un objeto de tipo Nullable<bool>
Así que ahora como un trabajo en torno a que decidió comprobar si o es anulable y crear una envoltura alrededor anulable obj.
Solución
Hay dos tipos de anulable -. Nullable<T>
y de tipo de referencia
Jon me ha corregido que es difícil de conseguir si el tipo caja, pero se puede con los genéricos:
- así que ¿qué más adelante. Esto es en realidad probando tipo T
, pero utilizando el parámetro obj
puramente para la inferencia de tipo genérico (para hacer más fácil para llamar) -. Funcionaría de manera casi idéntica sin el parámetro obj
, aunque
static bool IsNullable<T>(T obj)
{
if (obj == null) return true; // obvious
Type type = typeof(T);
if (!type.IsValueType) return true; // ref-type
if (Nullable.GetUnderlyingType(type) != null) return true; // Nullable<T>
return false; // value-type
}
Pero esto no funcionará tan bien si ya ha encajonado el valor de una variable de objeto.
Otros consejos
Hay una solución muy simple usando sobrecargas método
http://deanchalk.com/is-it-nullable/
extracto:
public static class ValueTypeHelper
{
public static bool IsNullable<T>(T t) { return false; }
public static bool IsNullable<T>(T? t) where T : struct { return true; }
}
entonces
static void Main(string[] args)
{
int a = 123;
int? b = null;
object c = new object();
object d = null;
int? e = 456;
var f = (int?)789;
bool result1 = ValueTypeHelper.IsNullable(a); // false
bool result2 = ValueTypeHelper.IsNullable(b); // true
bool result3 = ValueTypeHelper.IsNullable(c); // false
bool result4 = ValueTypeHelper.IsNullable(d); // false
bool result5 = ValueTypeHelper.IsNullable(e); // true
bool result6 = ValueTypeHelper.IsNullable(f); // true
La cuestión de "¿Cómo comprobar si un tipo es anulable?" en realidad es "¿Cómo comprobar si un tipo es Nullable<>
?", que se puede generalizar a "¿Cómo comprobar si un tipo es un tipo construido de algún tipo genérico?", de modo que no sólo responde a la pregunta "¿Es Nullable<int>
un Nullable<>
?", sino también "es un List<int>
List<>
?".
La mayor parte de la solución proporciona el uso del método Nullable.GetUnderlyingType()
, lo que, obviamente, sólo trabajar con el caso de Nullable<>
. No vi la solución de reflexión general, que funcionará con cualquier tipo genérico, por lo que decidí añadir aquí para la posteridad, a pesar de que esta pregunta ya ha sido contestada hace mucho tiempo.
Para comprobar si un tipo es una forma de Nullable<>
utilizando la reflexión, primero hay que convertir su tipo genérico construido, por ejemplo Nullable<int>
, en la definición de tipo genérico, Nullable<>
. Puede hacerlo utilizando el método de la clase GetGenericTypeDefinition()
Type
. A continuación, puede comparar el tipo resultante para Nullable<>
:
Type typeToTest = typeof(Nullable<int>);
bool isNullable = typeToTest.GetGenericTypeDefinition() == typeof(Nullable<>);
// isNullable == true
Lo mismo se puede aplicar a cualquier tipo genérico:
Type typeToTest = typeof(List<int>);
bool isList = typeToTest.GetGenericTypeDefinition() == typeof(List<>);
// isList == true
Existen varios tipos pueden parecer lo mismo, pero un número diferente de argumentos de tipo significa que es un tipo completamente diferente.
Type typeToTest = typeof(Action<DateTime, float>);
bool isAction1 = typeToTest.GetGenericTypeDefinition() == typeof(Action<>);
bool isAction2 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,>);
bool isAction3 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,,>);
// isAction1 == false
// isAction2 == true
// isAction3 == false
Desde objeto Type
se crea una instancia de una vez por tipo, se puede comprobar la igualdad de referencia entre ellos. Así que si usted quiere comprobar si dos objetos tienen la misma definición de tipo genérico, se puede escribir:
var listOfInts = new List<int>();
var listOfStrings = new List<string>();
bool areSameGenericType =
listOfInts.GetType().GetGenericTypeDefinition() ==
listOfStrings.GetType().GetGenericTypeDefinition();
// areSameGenericType == true
Si desea comprobar si un objeto es anulable, en lugar de un Type
, entonces se puede utilizar la técnica anterior, junto con la solución de Marc Gravell para crear un método bastante simple:
static bool IsNullable<T>(T obj)
{
if (!typeof(T).IsGenericType)
return false;
return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>);
}
Esto funciona para mí y parece simple:
static bool IsNullable<T>(T obj)
{
return default(T) == null;
}
Bueno, se podría utilizar:
return !(o is ValueType);
... sino un objeto en sí mismo no es anulable o de otra manera - un type es. Cómo planeabas usar este?
La forma más sencilla que puedo entender es:
public bool IsNullable(object obj)
{
Type t = obj.GetType();
return t.IsGenericType
&& t.GetGenericTypeDefinition() == typeof(Nullable<>);
}
Hay dos cuestiones: 1) las pruebas para ver si un tipo es anulable; y 2) la prueba para ver si un objeto representa un tipo anulable.
Por tema 1 (prueba de un tipo), he aquí una solución que he utilizado en mis propios sistemas: solución TypeIsNullable-cheque
Por tema 2 (prueba de un objeto), la solución de Dean tiza anterior funciona para los tipos de valor, pero no funciona para los tipos de referencia, ya que el uso de la etiqueta
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 en el código de cliente de prueba 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 he modificado el enfoque de Dean en IsObjectNullable
Los dos métodos anteriores podrían ser reemplazados con el siguiente método único y alcanzar la misma salida:
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 enfoque pasado,-único método es que el rendimiento se resiente cuando se utiliza un parámetro Anulable
CAVEAT: Este método funciona de forma fiable sólo si llama usando la referencia de objeto original o una copia exacta, como se muestra en los ejemplos. Sin embargo, si un objeto anulable se encajona a otro tipo (por ejemplo, objeto, etc.) en lugar de permanecer en su forma original Anulable <>, este método no funcionará de forma fiable. Si el código de llamada a este método no está utilizando el, referencia de objeto sin embalaje original o una copia exacta, no se puede determinar de forma fiable nulabilidad del objeto usando este método.
En la mayoría de los escenarios de codificación, para determinar nulabilidad lugar uno debe confiar en los ensayos de tipo de objeto original, no su referencia (por ejemplo, el código debe tener acceso al tipo original del objeto para determinar nulabilidad). En estos casos más comunes, IsTypeNullable (ver enlace) es un método fiable de determinar nulabilidad.
P.S. - Sobre "nulabilidad"
Debería repetir una declaración sobre nulabilidad que hice en un post aparte, que se aplica directamente a abordar adecuadamente este tema. Es decir, creo que el foco de la discusión aquí no debe ser la forma de comprobar si un objeto es un tipo anulable genérica, sino más bien si se puede asignar un valor nulo a un objeto de este tipo. En otras palabras, creo que debemos 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 la determinación de capacidad de nulos, que suele ser lo único que importa.
En un sistema que utiliza objetos con tipos posiblemente desconocidos hasta que en tiempo de ejecución (servicios web, llamadas remotas, bases de datos, se alimenta, etc.), un requisito común es determinar si un nulo se puede asignar al objeto, o si la objeto podría contener un valor nulo. La realización de tales operaciones sobre los tipos no anulable probablemente producirá errores, por lo general excepciones, que son muy costosos tanto en términos de resultados y requisitos de codificación. Para tomar el enfoque altamente preferida de evitar de forma proactiva este tipo de problemas, es necesario determinar si un objeto de un ArbitTipo Rary es capaz de contener un valor nulo; es decir, si se trata generalmente 'anulable'.
En un sentido muy práctico y típico, en términos de capacidad de nulos .NET no se desprende necesariamente que ese tipo de un objeto es una forma de anulable. En muchos casos, de hecho, los objetos tienen tipos de referencia, pueden contener un valor nulo, y por lo tanto son todos anulable; ninguno de ellos tiene un tipo anulable. Por lo tanto, a efectos prácticos, en la mayoría de los escenarios, las pruebas deben hacerse para el concepto general de anulabilidad, contra el concepto dependiente de la implementación de anulable. Por lo que no se debe colgar por centrarse únicamente en el tipo .NET anulables sino incorporamos nuestra comprensión de sus necesidades y el comportamiento en el proceso de centrarse en el concepto general, práctica de nulabilidad.
La solución más simple que se me ocurrió es implementar la solución de Microsoft ( Cómo identificar un tipo anulable (C # Guía de Programación) ) como un método de extensión:
public static bool IsNullable(this Type type)
{
return Nullable.GetUnderlyingType(type) != null;
}
Esto puede ser llamado de esta manera:
bool isNullable = typeof(int).IsNullable();
Esto también parece una manera lógica para acceder IsNullable()
porque encaja con todos los demás métodos de la clase IsXxxx()
Type
.
Tenga cuidado, cuando el boxeo un tipo anulable (Nullable<int>
o int por ejemplo?):
int? nullValue = null;
object boxedNullValue = (object)nullValue;
Debug.Assert(boxedNullValue == null);
int? value = 10;
object boxedValue = (object)value;
Debug.Assert( boxedValue.GetType() == typeof(int))
Se convierte en un verdadero tipo de referencia, por lo que pierden el hecho de que era anulable.
Tal vez un poco fuera de tema, pero todavía algo de información interesante. Encuentro una gran cantidad de personas que utilizan Nullable.GetUnderlyingType() != null
a la identidad si un tipo es anulable. Esto funciona obviamente, pero Microsoft aconseja a los siguientes type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)
(ver http://msdn.microsoft .com / es-es / library / ms366789.aspx ).
Miré a esto desde un lado de vista del rendimiento. La conclusión de la prueba (un millón de intentos) a continuación es que cuando un tipo es un anulable, la opción Microsoft ofrece el mejor rendimiento.
Nullable.GetUnderlyingType (): 1335ms (3 veces más lento)
GetGenericTypeDefinition () == typeof (Anulable <>): 500 ms
Sé que estamos hablando de una pequeña cantidad de tiempo, pero todo el mundo ama a ajustar los milisegundos :-)! Así que si eres jefe quiere que reducir algunos milisegundos, entonces este es tu salvador ...
/// <summary>Method for testing the performance of several options to determine if a type is nullable</summary>
[TestMethod]
public void IdentityNullablePerformanceTest()
{
int attempts = 1000000;
Type nullableType = typeof(Nullable<int>);
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
for (int attemptIndex = 0; attemptIndex < attempts; attemptIndex++)
{
Assert.IsTrue(Nullable.GetUnderlyingType(nullableType) != null, "Expected to be a nullable");
}
Console.WriteLine("Nullable.GetUnderlyingType(): {0} ms", stopwatch.ElapsedMilliseconds);
stopwatch.Restart();
for (int attemptIndex = 0; attemptIndex < attempts; attemptIndex++)
{
Assert.IsTrue(nullableType.IsGenericType && nullableType.GetGenericTypeDefinition() == typeof(Nullable<>), "Expected to be a nullable");
}
Console.WriteLine("GetGenericTypeDefinition() == typeof(Nullable<>): {0} ms", stopwatch.ElapsedMilliseconds);
stopwatch.Stop();
}
Esta versión:
- Resultados de la caché es más rápido,
- no requiere las variables innecesarias, como el Método (T obj)
- No es complicado:),
- clase genérica sin movimiento, que tiene campos calculados una vez
:
public static class IsNullable<T>
{
private static readonly Type type = typeof(T);
private static readonly bool is_nullable = type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
public static bool Result { get { return is_nullable; } }
}
bool is_nullable = IsNullable<int?>.Result;
Esto es lo que se me ocurrió, como todo lo demás parecía a fallar - por lo menos en el PLC - Portable biblioteca de clases / .NET Core con> = C # 6
Solución: Extender métodos estáticos para cualquier T
Tipo y Nullable<T>
y utilizar el hecho de que el método de extensión estática, que coincida con el tipo subyacente va a ser invocado y toma precedencia sobre la extensión-método T
genérico .
Para T
:
public static partial class ObjectExtension
{
public static bool IsNullable<T>(this T self)
{
return false;
}
}
y para Nullable<T>
public static partial class NullableExtension
{
public static bool IsNullable<T>(this Nullable<T> self) where T : struct
{
return true;
}
}
El uso de Reflexión y type.IsGenericType
... no funcionó en mi conjunto actual de .NET tiempos de ejecución. Tampoco el MSDN ayuda Documentación .
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {…}
En parte debido a que el API de reflexión se ha cambiado de forma significativa en .NET Core.
Creo que los que utilizan las pruebas sugerido por Microsoft contra IsGenericType
son buenos, pero en el código para GetUnderlyingType
, Microsoft utiliza una prueba adicional para asegurarse de que no pase en el genérica Nullable<>
definición de tipo:
public static bool IsNullableType(this Type nullableType) =>
// instantiated generic type only
nullableType.IsGenericType &&
!nullableType.IsGenericTypeDefinition &&
Object.ReferenceEquals(nullableType.GetGenericTypeDefinition(), typeof(Nullable<>));
una forma sencilla de hacer esto:
public static bool IsNullable(this Type type)
{
if (type.IsValueType) return Activator.CreateInstance(type) == null;
return true;
}
estos son mis pruebas de unidad y todos pasaron
IsNullable_String_ShouldReturn_True
IsNullable_Boolean_ShouldReturn_False
IsNullable_Enum_ShouldReturn_Fasle
IsNullable_Nullable_ShouldReturn_True
IsNullable_Class_ShouldReturn_True
IsNullable_Decimal_ShouldReturn_False
IsNullable_Byte_ShouldReturn_False
IsNullable_KeyValuePair_ShouldReturn_False
pruebas de unidad real
[TestMethod]
public void IsNullable_String_ShouldReturn_True()
{
var typ = typeof(string);
var result = typ.IsNullable();
Assert.IsTrue(result);
}
[TestMethod]
public void IsNullable_Boolean_ShouldReturn_False()
{
var typ = typeof(bool);
var result = typ.IsNullable();
Assert.IsFalse(result);
}
[TestMethod]
public void IsNullable_Enum_ShouldReturn_Fasle()
{
var typ = typeof(System.GenericUriParserOptions);
var result = typ.IsNullable();
Assert.IsFalse(result);
}
[TestMethod]
public void IsNullable_Nullable_ShouldReturn_True()
{
var typ = typeof(Nullable<bool>);
var result = typ.IsNullable();
Assert.IsTrue(result);
}
[TestMethod]
public void IsNullable_Class_ShouldReturn_True()
{
var typ = typeof(TestPerson);
var result = typ.IsNullable();
Assert.IsTrue(result);
}
[TestMethod]
public void IsNullable_Decimal_ShouldReturn_False()
{
var typ = typeof(decimal);
var result = typ.IsNullable();
Assert.IsFalse(result);
}
[TestMethod]
public void IsNullable_Byte_ShouldReturn_False()
{
var typ = typeof(byte);
var result = typ.IsNullable();
Assert.IsFalse(result);
}
[TestMethod]
public void IsNullable_KeyValuePair_ShouldReturn_False()
{
var typ = typeof(KeyValuePair<string, string>);
var result = typ.IsNullable();
Assert.IsFalse(result);
}