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.

¿Fue útil?

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 sobrecarga siempre devuelve falso. Como los tipos de referencia son inherentemente anulable, probando un tipo de referencia siempre debe devolver true. Por favor, vea la nota [Acerca de "nulabilidad"] abajo para una explicación de esta semántica. Por lo tanto, aquí está mi modificación al método 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 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 (T t) es que su enfoque original siempre se devuelve false para un tipo de referencia. Desde un método como IsObjectNullable debe ser capaz de manejar los valores de tipo de referencia y ya que todos los tipos de referencia son inherentemente anulable, a continuación, si se hace pasar ya sea un tipo de referencia o un valor nulo, el método debe siempre devolver verdadero.

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 . Se necesita mucho más tiempo de procesador para ejecutar la última línea de este método único que lo hace para permitir que el compilador para elegir la segunda sobrecarga del método mostrado previamente cuando un de tipo anulable parámetro se utiliza en la llamada IsObjectNullable. Por lo tanto, la solución óptima es utilizar el enfoque de dos método ilustrado aquí.

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);
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top