¿Cómo verificar si un objeto es anulable?

203

¿Cómo puedo verificar si un objeto dado es anulable? En otras palabras, cómo implementar el siguiente método ...

bool IsNullableValueType(object o)
{
    ...
}

EDITAR: Estoy buscando tipos de valores anulables. No tenía tipos de referencia en mente.

//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 un valor igual a true. Lo que realmente quería era un objeto de tipoNullable<bool>

Así que ahora, como alternativa, decidí verificar si o es anulable y crear un contenedor anulable alrededor de obj.

Autodidacta
fuente
¿Debería el código incluir cadenas como anulables? Son un ValueType no genérico que parece ser anulable. ¿O no son un ValueType?
TamusJRoyce
La cadena no es un ValueType. Es un tipo de referencia.
Suncat2000
¡Esta es una muy buena pregunta! El 'Type.IsNullableType ()' es un poco engañoso porque en realidad solo verifica que el tipo sea 'Nullable <T>', que no devolvió los resultados esperados si realmente desea verificar cualquier tipo que pueda aceptar un valor nulo valor (por ejemplo, intenté usar con a.IsNullableType (), donde 'a' era un 'typeof (string)' determinado en tiempo de ejecución)
ErrCode
La respuesta está en fieldInfo.FieldType: verifique si FieldType es genérico y si el tipo genérico es de tipo Nullable <>. (Ejemplo: if (FieldType.IsGenericType && FieldType.GetGenericTypeDefinition () == typeof (Nullable <>))). No intente obtener obj.GetType () tendrá UndelyingSystemType de Nullable <T> variable T (en su caso de tipo booleano, en lugar de Nullable <Boolean>), es un problema de boxeo.
SoLaR

Respuestas:

272

Hay dos tipos de anulables, Nullable<T>y de tipo de referencia.

Jon me ha corregido que es difícil obtener el tipo de letra si está en caja, pero puede hacerlo con los genéricos: - ¿qué tal a continuación? Esto es en realidad una prueba de tipo T, pero usando el objparámetro solo para inferencia de tipo genérico (para que sea más fácil llamar), sin embargo, funcionaría casi de manera idéntica sin el objparámetro.

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 encuadrado el valor en una variable de objeto.

Documentación de Microsoft: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/how-to-identify-a-nullable-type

Marc Gravell
fuente
77
La última línea solo es válida si de alguna manera logras obtener un Nullable <T> en caja en lugar de boxear directamente en T. Es posible, pero difícil de lograr por lo que recuerdo.
Jon Skeet
Este código fue útil para mí, no porque obtuve un Nullable <T> en caja sino porque estaba escribiendo una clase básica de convertidor WPF genérico y algunas propiedades son anulables, por lo que utilicé Nullable.GetUnderlyingType para detectar ese caso y Activator.CreateInstance para hacer un nulo en caja, (Convert.ChangeType no maneja nulables por cierto).
Qwertie
1
@Abel, si te refieres a su edición para aclarar que no había considerado los tipos de referencia, creo que mi respuesta es anterior a esa edición; sospecho que el lector puede tomar su propia decisión allí, en función de sus propias necesidades (confirmado: su comentario vuelve a escribir como se agregó a las 14:42; mi respuesta fue <= 14:34)
Marc Gravell
1
¿(Obj == nulo) arrojará una excepción cuando obj = 1?
Qi Fan
3
@JustinMorgan Si Tes un parámetro genérico restringido por T : struct, entonces Tno está permitido Nullable<>, por lo que no necesita verificación en ese caso. Sé que el tipo Nullable<>es una estructura, pero en C # la restricción where T : structexcluye específicamente los tipos de valores anulables. La especificación dice: "Tenga en cuenta que aunque se clasifica como un tipo de valor, un tipo anulable (§4.1.10) no satisface la restricción del tipo de valor".
Jeppe Stig Nielsen
46

Existe una solución muy simple que utiliza sobrecargas de métodos.

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

luego

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
Dean Chalk
fuente
77
más uno para usted señor por agregar casos de prueba. He usado esos casos de prueba para verificar todas las otras respuestas. Más personas deberían ir un poco más.
Marty Neal
44
Por lo que vale, esto no funciona en VB.NET. Da como resultado un error del compilador de " Falló la resolución de sobrecarga porque no hay 'IsNullable' accesible más específico para estos argumentos " en todas las situaciones en las Trueque se devolvería.
ckittel
1
Realmente me gusta esta solución, y es una pena que VB no pueda manejarla. Intenté trabajar con ValueType, pero tuve problemas con el compilador de VB por ser inconsistente sobre qué sobrecarga usar según si se llamaba como un método compartido o una extensión, incluso planteé una pregunta sobre esto, ya que parece extraño: stackoverflow.com/ preguntas / 12319591 / ...
James Close
22
Está comprobando el tipo de tiempo de compilación , pero ya es obvio (de intellisense) si el tipo de tiempo de compilación es anulable ( System.Nullable<>). Si dices object g = e;y entonces ValueTypeHelper.IsNullable(g), ¿qué esperas obtener?
Jeppe Stig Nielsen
18
Acabo de verificar; esto no funciona , como dijo Jeppe. Si las variables se convierten en objeto, siempre devolverá falso. Por lo tanto, no puede determinar el tipo de un objeto desconocido en tiempo de ejecución de esta manera. El único momento en que esto funciona es si el tipo se repara en tiempo de compilación y, en ese caso, no necesita ninguna verificación de tiempo de ejecución.
HugoRune
30

La pregunta "¿Cómo verificar si un tipo es anulable?" en realidad es "¿Cómo verificar si un tipo es Nullable<>?", que se puede generalizar a "¿Cómo verificar si un tipo es un tipo construido de algún tipo genérico?", de modo que no solo responda la pregunta "¿Es Nullable<int>un Nullable<>?", pero también "¿Es List<int>un List<>?".

La mayoría de las soluciones proporcionadas utilizan el Nullable.GetUnderlyingType()método, que obviamente solo funcionará con el caso de Nullable<>. No vi la solución reflexiva general que funcionaría con cualquier tipo genérico, por lo que decidí agregarla aquí para la posteridad, a pesar de que esta pregunta ya ha sido respondida hace mucho tiempo.

Para comprobar si un tipo es una forma de Nullable<>utilizar 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 GetGenericTypeDefinition()método de la Typeclase. Luego puede comparar el tipo resultante con 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

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

Dado que los Typeobjetos se instancian una vez por tipo, puede verificar la igualdad de referencia entre ellos. Entonces, si desea verificar si dos objetos son de la misma definición de tipo genérico, puede escribir:

var listOfInts = new List<int>();
var listOfStrings = new List<string>();

bool areSameGenericType =
    listOfInts.GetType().GetGenericTypeDefinition() ==
    listOfStrings.GetType().GetGenericTypeDefinition();
// areSameGenericType == true

Si desea verificar si un objeto es anulable, en lugar de un Type, entonces puede usar 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<>);
}
Allon Guralnek
fuente
@ AllonGuralnek Hay una versión simplificada en mi respuesta. Quería hacerlo como edición y como mi reputación no es su nivel, sería editado sin mi nombre en su respuesta, aun así, parece que la revisión siempre me está disparando, que está dirigiendo al autor incluso si era no. Mundo extraño, algunas personas no obtienen definiciones :).
ipavlu
@ipavlu: su versión no está simplificada, de hecho es más complicada. Creo que quieres decir que está optimizado ya que guardas en caché el resultado. Eso hace que sea más difícil de entender.
Allon Guralnek
@ AllonGuralnek clase genérica estática y campos estáticos inicializados una vez, ¿eso es complicado? Querido Dios, cometí un crimen terrible :).
ipavlu
@ipavku: Sí, porque no tiene nada que ver con la pregunta "¿Cómo verificar si un objeto es anulable?". Trato de mantenerlo simple y al grano, y evito introducir conceptos innecesarios y no relacionados.
Allon Guralnek
1
@nawfal: Si te entendí correctamente, estás cuestionando mi implementación ante la existencia de lo Nullable.GetUnderlyingType()que ya proporciona el marco. ¿Por qué no simplemente usar el método en el marco? Bueno deberías. Es más claro, más conciso y mejor probado. Pero en mi publicación estoy tratando de enseñar cómo usar la reflexión para obtener la información que desea, para que alguien pueda aplicarla a cualquier tipo (reemplazándola typeof(Nullable<>)por cualquier otro tipo). Si observa las fuentes de GetUnderlyingType()(original o descompilado), verá que es muy similar a mi código.
Allon Guralnek
30

Esto funciona para mí y parece simple:

static bool IsNullable<T>(T obj)
{
    return default(T) == null;
}

Para tipos de valor:

static bool IsNullableValueType<T>(T obj)
{
    return default(T) == null && typeof(T).BaseType != null && "ValueType".Equals(typeof(T).BaseType.Name);
}
Erik
fuente
77
Para lo que vale, esta es también la prueba utilizada por Microsoft
canton7
1
Bien ... ¿No es esta la respuesta principal porque vino después? La respuesta principal me parece muy confusa.
Vincent Buscarello
1
Esta debería ser la mejor respuesta. Después de días de intentar diferentes métodos que pensé al azar de esta solución, lo intentó, y parece estar funcionando perfectamente (en comparación con la respuesta más altas calificaciones)
user3163495
2
Esta es una excelente solución para averiguar si alguna instancia se puede establecer en NULL, pero será verdadera para todo lo que se puede establecer en nulo, incluidos los objetos ordinarios. Es importante darse cuenta de que la pregunta original específicamente quería detectar Nullable ValueTypes.
JamesHoux
20

Bueno, podrías usar:

return !(o is ValueType);

... pero un objeto en sí no es anulable o no, un tipo sí lo es. ¿Cómo planeabas usar esto?

Jon Skeet
fuente
2
Esto me arrojó un poco. por ejemplo, int? i = 5; typeof (i) devuelve System.Int32 en lugar de Nullable <Int32> - typeof (int?) devuelve Nullable <Int32> ... ¿dónde puedo obtener alguna claridad sobre este tema?
Gishu
2
typeof (i) dará un error de compilación: no puede usar typeof con una variable. ¿Qué hiciste realmente?
Jon Skeet
15
i.GetType () se encuadrará en Object primero, y no existe un tipo anulable en caja: Nullable <int> se encuadra en una referencia nula o en un int encuadrado.
Jon Skeet
Esa forma es mejor que Nullable.GetUnderlyingType (type)! = Null?
Kiquenet
@ Kiquenet: No tenemos el tipo aquí, solo el valor.
Jon Skeet
11

La forma más simple que puedo entender es:

public bool IsNullable(object obj)
{
    Type t = obj.GetType();
    return t.IsGenericType 
        && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}
CARLOS LOTH
fuente
+1. Excelente solución para los tipos nulos en caja. No he probado esto específicamente todavía. Entonces, si alguien más puede verificar, sería apreciado.
TamusJRoyce
Ya lo he probado. Tuve que crear una especie de Nullabletipo, pero con semántica diferente. En mi situación, debería admitir nullcomo un valor válido y también no admitir ningún valor. Entonces se creó un Optionaltipo. Como era necesario soportar nullvalores, también tuve que implementar código para manejar Nullablevalores como parte de mi implementación. De ahí es de donde vino este código.
CARLOS LOTH
9
Creo que esta solución está mal. Pasar un tipo de valor Nullable como argumento a un método que espera un parámetro de objeto de tipo debería causar el boxeo. Anulable es un tipo de valor y el resultado de la conversión de boxeo es un tipo de referencia. No hay nulables en caja. Creo que este método siempre devuelve falso?
Mishax
1
¿Alguna prueba al respecto como otras respuestas?
Kiquenet
55
No funciona debido al valor del boxeo. Siempre devolverá FALSE.
N Rocking
10

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.

Mark Jones
fuente
8

La solución más simple que se me ocurrió es implementar la solución de Microsoft ( Cómo: identificar un tipo anulable (Guía de programación de C #) ) como método de extensión:

public static bool IsNullable(this Type type)
{
    return Nullable.GetUnderlyingType(type) != null;
}

Esto se puede llamar así:

bool isNullable = typeof(int).IsNullable();

Esto también parece una forma lógica de acceso IsNullable()porque encaja con todos los otros IsXxxx()métodos de la Typeclase.

sclarke81
fuente
1
¿No quieres usar "==" en lugar de "! ="?
vkelman
Buen lugar @vkelman En lugar de hacer ese cambio, actualicé la respuesta para usar la sugerencia actual de Microsoft, ya que esto ha cambiado desde que escribí esto.
sclarke81
6

Tenga cuidado, al boxear 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 pierde el hecho de que era anulable.

pensar antes de codificar
fuente
3

Tal vez un poco fuera de tema, pero aún así alguna información interesante. Encuentro a muchas personas que utilizan Nullable.GetUnderlyingType() != nullpara identificar si un tipo es anulable. Obviamente, esto funciona, pero Microsoft aconseja lo siguiente type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)(consulte http://msdn.microsoft.com/en-us/library/ms366789.aspx ).

Miré esto desde el punto de vista del rendimiento. La conclusión de la prueba (un millón de intentos) a continuación es que cuando un tipo es anulable, la opción de Microsoft ofrece el mejor rendimiento.

Nullable.GetUnderlyingType (): 1335ms (3 veces más lento)

GetGenericTypeDefinition () == typeof (Nullable <>): 500ms

Sé que estamos hablando de una pequeña cantidad de tiempo, pero a todos les encanta ajustar los milisegundos :-)! Entonces, si su jefe quiere que reduzca algunos milisegundos, entonces este es su 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();
}
Roel van Megen
fuente
1
Hola, probablemente haya un problema con la medición del tiempo, la afirmación puede afectar los resultados. ¿Has probado sin Assert? También Console.WriteLine debe estar fuera del área medida. +1 para un intento de cuantificar problemas de rendimiento :).
ipavlu
@ipavlu Console.WriteLineestá fuera del área de
medición
Roel, como ha mencionado ipavlu, Assertdebería estar fuera del circuito. En segundo lugar, también debe probarlo contra no anulables, así como para detectar casos falsos. Hice una prueba similar (2 anulables y 4 no anulables) y obtengo ~ 2 segundos GetUnderlyingTypey ~ 1 segundo GetGenericTypeDefinition, es decir, GetGenericTypeDefinitiones dos veces más rápido (no tres veces).
nawfal
Hice otra ronda con 2 anulables y 2 no anulables, esta vez GetUnderlyingTypefue 2.5 veces más lento. Con solo no anulables, esta vez ambos son cuello y cuello.
nawfal
Pero lo más importante, GetUnderlyingTypees útil cuando tiene que verificar la nulabilidad y obtener el tipo subyacente si es anulable. Esto es muy útil y puede ver patrones a menudo Activator.CreateInstance(Nullable.GetUnderlyingType(type) ?? type). Es como la aspalabra clave, comprueba el reparto, así como lo hace y devuelve el resultado. Si desea obtener el tipo subyacente de anulable en ese momento, hacer una GetGenericTypeDefinitionverificación y luego obtener el tipo genérico será una mala idea. También GetUnderlyingTypees mucho más legible y memorable. No me importaría si lo estoy haciendo solo ~ 1000 veces.
nawfal
0

Esta versión:

  • el almacenamiento en caché de resultados es más rápido,
  • no requiere variables innecesarias, como Método (T obj)
  • NO COMPLICADO :),
  • solo clase genérica estática, que tiene campos calculados una sola 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;
ipavlu
fuente
Creo que te respondiste a ti mismo con esa declaración estática 'is_nullable'. Consejo: ¿declarar objetos con int? (objeto a = (int?) 8;) y ver qué pasa.
SoLaR
0

Esto es lo que se me ocurrió, ya que todo lo demás parecía fallar, al menos en el PLC - Biblioteca de clases portátil / .NET Core con> = C # 6

Solución: amplíe los métodos estáticos para cualquier tipo Ty Nullable<T>utilice el hecho de que el método de extensión estática, que coincide con el tipo subyacente, se invocará y prevalecerá sobre el Tmétodo de extensión 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;
    }
}

Usar Reflection y type.IsGenericType... no funcionó en mi conjunto actual de .NET Runtimes. Tampoco ayudó la documentación de MSDN .

if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {…}

En parte porque la API de Reflection ha cambiado bastante significativamente en .NET Core.

Lorenz Lo Sauer
fuente
0

Creo que los que usan las pruebas sugeridas de Microsoft IsGenericTypeson buenos, pero en el código para GetUnderlyingType, Microsoft usa una prueba adicional para asegurarse de que no pasó la definición de tipo genérico Nullable<>:

 public static bool IsNullableType(this Type nullableType) =>
    // instantiated generic type only                
    nullableType.IsGenericType &&
    !nullableType.IsGenericTypeDefinition &&
    Object.ReferenceEquals(nullableType.GetGenericTypeDefinition(), typeof(Nullable<>));
NetMage
fuente
-1

Una forma simple de hacer esto:

    public static bool IsNullable(this Type type)
    {
        if (type.IsValueType) return Activator.CreateInstance(type) == null;

        return true;
    }

estas son mis pruebas unitarias y todas 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 unitarias reales

    [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);
    }
VJPPaz
fuente