¿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.
Respuestas:
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 elobj
pará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 elobj
parámetro.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
fuente
T
es un parámetro genérico restringido porT : struct
, entoncesT
no está permitidoNullable<>
, por lo que no necesita verificación en ese caso. Sé que el tipoNullable<>
es una estructura, pero en C # la restricciónwhere T : struct
excluye 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".Existe una solución muy simple que utiliza sobrecargas de métodos.
http://deanchalk.com/is-it-nullable/
extracto:
luego
fuente
True
que se devolvería.System.Nullable<>
). Si dicesobject g = e;
y entoncesValueTypeHelper.IsNullable(g)
, ¿qué esperas obtener?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 "¿EsNullable<int>
unNullable<>
?", pero también "¿EsList<int>
unList<>
?".La mayoría de las soluciones proporcionadas utilizan el
Nullable.GetUnderlyingType()
método, que obviamente solo funcionará con el caso deNullable<>
. 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 ejemploNullable<int>
, en la definición de tipo genérico,Nullable<>
. Puede hacerlo utilizando elGetGenericTypeDefinition()
método de laType
clase. Luego puede comparar el tipo resultante conNullable<>
:Lo mismo se puede aplicar a cualquier tipo genérico:
Varios tipos pueden parecer lo mismo, pero un número diferente de argumentos de tipo significa que es un tipo completamente diferente.
Dado que los
Type
objetos 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: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:fuente
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ándolatypeof(Nullable<>)
por cualquier otro tipo). Si observa las fuentes deGetUnderlyingType()
(original o descompilado), verá que es muy similar a mi código.Esto funciona para mí y parece simple:
Para tipos de valor:
fuente
Bueno, podrías usar:
... pero un objeto en sí no es anulable o no, un tipo sí lo es. ¿Cómo planeabas usar esto?
fuente
La forma más simple que puedo entender es:
fuente
Nullable
tipo, pero con semántica diferente. En mi situación, debería admitirnull
como un valor válido y también no admitir ningún valor. Entonces se creó unOptional
tipo. Como era necesario soportarnull
valores, también tuve que implementar código para manejarNullable
valores como parte de mi implementación. De ahí es de donde vino este código.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:
Y aquí está mi modificación al código de prueba del cliente para la solución anterior:
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:
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.
fuente
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:
Esto se puede llamar así:
Esto también parece una forma lógica de acceso
IsNullable()
porque encaja con todos los otrosIsXxxx()
métodos de laType
clase.fuente
Tenga cuidado, al boxear un tipo anulable (
Nullable<int>
o int? Por ejemplo):Se convierte en un verdadero tipo de referencia, por lo que pierde el hecho de que era anulable.
fuente
Tal vez un poco fuera de tema, pero aún así alguna información interesante. Encuentro a muchas personas que utilizan
Nullable.GetUnderlyingType() != null
para identificar si un tipo es anulable. Obviamente, esto funciona, pero Microsoft aconseja lo siguientetype.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 ...
fuente
Console.WriteLine
está fuera del área deAssert
deberí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 segundosGetUnderlyingType
y ~ 1 segundoGetGenericTypeDefinition
, es decir,GetGenericTypeDefinition
es dos veces más rápido (no tres veces).GetUnderlyingType
fue 2.5 veces más lento. Con solo no anulables, esta vez ambos son cuello y cuello.GetUnderlyingType
es útil cuando tiene que verificar la nulabilidad y obtener el tipo subyacente si es anulable. Esto es muy útil y puede ver patrones a menudoActivator.CreateInstance(Nullable.GetUnderlyingType(type) ?? type)
. Es como laas
palabra clave, comprueba el reparto, así como lo hace y devuelve el resultado. Si desea obtener el tipo subyacente de anulable en ese momento, hacer unaGetGenericTypeDefinition
verificación y luego obtener el tipo genérico será una mala idea. TambiénGetUnderlyingType
es mucho más legible y memorable. No me importaría si lo estoy haciendo solo ~ 1000 veces.Esta versión:
:
fuente
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
T
yNullable<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 elT
método de extensión genérico .Para
T
:y para
Nullable<T>
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.
fuente
Creo que los que usan las pruebas sugeridas de Microsoft
IsGenericType
son buenos, pero en el código paraGetUnderlyingType
, Microsoft usa una prueba adicional para asegurarse de que no pasó la definición de tipo genéricoNullable<>
:fuente
Una forma simple de hacer esto:
estas son mis pruebas unitarias y todas pasaron
pruebas unitarias reales
fuente