2008-09-20 20 views
65

Una vez más, uno de esos: "¿Existe una forma más fácil de hacer las cosas en lugar de mi método de ayuda?"¿Cómo convierto un System.Type a su versión que admite nulos?

Por lo tanto, es fácil obtener el tipo subyacente de un tipo anulable, pero ¿cómo obtengo la versión con nulos de un tipo .NET?

Así que tengo

typeof(int) 
typeof(DateTime) 
System.Type t = something; 

y quiero

int? 
DateTime? 

o

Nullable<int> (which is the same) 
if (t is primitive) then Nullable<T> else just T 

¿Hay un método integrado?

Respuesta

91

Este es el código que utilizo:

Type GetNullableType(Type type) { 
    // Use Nullable.GetUnderlyingType() to remove the Nullable<T> wrapper if type is already nullable. 
    type = Nullable.GetUnderlyingType(type); 
    if (type.IsValueType) 
     return typeof(Nullable<>).MakeGenericType(type); 
    else 
     return type; 
} 
+0

¡Agradable! Esta es una solución realmente buena. – ljs

+0

¡Gran respuesta! Justo lo que estaba buscando. –

+0

Solución fría. ¿Qué tal si se trata de un método de extensión en Type? Parece adecuado en esta situación. –

2

No hay nada construido en el que yo sepa, como el int?, etc. es simplemente azúcar sintáctica para Nullable<T>; y no recibe un tratamiento especial más allá de eso. Es especialmente improbable dado que intenta obtener esto a partir de la información de tipo de un tipo determinado. Por lo general, siempre se necesita un código 'roll your own' como dado. Debería usar Reflection para crear un nuevo tipo Nullable con un tipo de parámetro del tipo de entrada.

Editar: Como sugiere el comentario hecho Nullable<> es especialmente tratado, y en el tiempo de ejecución para arrancar como se explica en this article.

+0

Actaully, estoy bastante seguro de que el CLR tiene algo de magia especial para manejar Nullable <> de una manera algo diferente. Tendré que verificar esto. – TraumaPony

+0

Estaría interesado en esto, feliz de admitir que estoy equivocado si ese es el caso :-) – ljs

+0

El CLR tiene un manejo especial para Nullables. http://blogs.msdn.com/somasegar/archive/2005/08/11/450640.aspx –

7

La respuesta de Lyman es genial y me ha ayudado, sin embargo, hay un error más que debe corregirse.

Nullable.GetUnderlyingType(type) solo debe llamarse si el tipo ya no es de tipo Nullable. De lo contrario, parece devolver erróneamente nulo cuando el tipo deriva de System.RuntimeType (como cuando paso en typeof(System.Int32)). La siguiente versión evita tener que llamar al Nullable.GetUnderlyingType(type) marcando si el tipo es Nullable en su lugar.

A continuación encontrará una versión ExtensionMethod de este método que inmediatamente devolver el tipo menos que se trata de un ValueType que no está ya Nullable.

Type NullableVersion(this Type sourceType) 
{ 
    if(sourceType == null) 
    { 
     // Throw System.ArgumentNullException or return null, your preference 
    } 
    else if(sourceType == typeof(void)) 
    { // Special Handling - known cases where Exceptions would be thrown 
     return null; // There is no Nullable version of void 
    } 

    return !sourceType.IsValueType 
      || (sourceType.IsGenericType 
       && sourceType.GetGenericTypeDefinition() == typeof(Nullable<>)) 
     ? sourceType 
     : typeof(Nullable<>).MakeGenericType(sourceType); 
} 

(lo siento, pero no podía simplemente colgar un comentario en la respuesta de Lyman porque yo era nuevo y no tenía representante suficiente todavía.)

14

Tengo un par de métodos que He escrito en mi biblioteca de utilidad en la que he confiado mucho. El primero es un método que convierte cualquier tipo a su correspondiente Anulable <Tipo> formulario:

/// <summary> 
    /// [ <c>public static Type GetNullableType(Type TypeToConvert)</c> ] 
    /// <para></para> 
    /// Convert any Type to its Nullable&lt;T&gt; form, if possible 
    /// </summary> 
    /// <param name="TypeToConvert">The Type to convert</param> 
    /// <returns> 
    /// The Nullable&lt;T&gt; converted from the original type, the original type if it was already nullable, or null 
    /// if either <paramref name="TypeToConvert"/> could not be converted or if it was null. 
    /// </returns> 
    /// <remarks> 
    /// To qualify to be converted to a nullable form, <paramref name="TypeToConvert"/> must contain a non-nullable value 
    /// type other than System.Void. Otherwise, this method will return a null. 
    /// </remarks> 
    /// <seealso cref="Nullable&lt;T&gt;"/> 
    public static Type GetNullableType(Type TypeToConvert) 
    { 
     // Abort if no type supplied 
     if (TypeToConvert == null) 
      return null; 

     // If the given type is already nullable, just return it 
     if (IsTypeNullable(TypeToConvert)) 
      return TypeToConvert; 

     // If the type is a ValueType and is not System.Void, convert it to a Nullable<Type> 
     if (TypeToConvert.IsValueType && TypeToConvert != typeof(void)) 
      return typeof(Nullable<>).MakeGenericType(TypeToConvert); 

     // Done - no conversion 
     return null; 
    } 

El segundo método simplemente informa de si un tipo dado es anulable.Este método es llamado por el primero y es útil por separado:

/// <summary> 
    /// [ <c>public static bool IsTypeNullable(Type TypeToTest)</c> ] 
    /// <para></para> 
    /// Reports whether a given Type is nullable (Nullable&lt; Type &gt;) 
    /// </summary> 
    /// <param name="TypeToTest">The Type to test</param> 
    /// <returns> 
    /// true = The given Type is a Nullable&lt; Type &gt;; false = The type is not nullable, or <paramref name="TypeToTest"/> 
    /// is null. 
    /// </returns> 
    /// <remarks> 
    /// This method tests <paramref name="TypeToTest"/> and reports whether it is nullable (i.e. whether it is either a 
    /// reference type or a form of the generic Nullable&lt; T &gt; type). 
    /// </remarks> 
    /// <seealso cref="GetNullableType"/> 
    public static bool IsTypeNullable(Type TypeToTest) 
    { 
     // Abort if no type supplied 
     if (TypeToTest == null) 
      return false; 

     // If this is not a value type, it is a reference type, so it is automatically nullable 
     // (NOTE: All forms of Nullable<T> are value types) 
     if (!TypeToTest.IsValueType) 
      return true; 

     // Report whether TypeToTest is a form of the Nullable<> type 
     return TypeToTest.IsGenericType && TypeToTest.GetGenericTypeDefinition() == typeof(Nullable<>); 
    } 

Lo anterior IsTypeNullable aplicación funciona como un campeón cada vez, pero es ligeramente más detallado y lento en su última línea de código. El siguiente órgano de código es el mismo que el anterior para IsTypeNullable, excepto la última línea de código es más sencillo y rápido:

 // Abort if no type supplied 
     if (TypeToTest == null) 
      return false; 

     // If this is not a value type, it is a reference type, so it is automatically nullable 
     // (NOTE: All forms of Nullable<T> are value types) 
     if (!TypeToTest.IsValueType) 
      return true; 

     // Report whether an underlying Type exists (if it does, TypeToTest is a nullable Type) 
     return Nullable.GetUnderlyingType(TypeToTest) != null; 

Enjoy!

Marcos

P. S. - Acerca de "Nullability"

Debo repetir una declaración sobre la capacidad de anulación que hice en una publicación separada, que se aplica directamente para abordar este tema de forma adecuada. Es decir, creo que el foco de la discusión aquí no debería ser cómo verificar si un objeto es un tipo anulable genérico, sino más bien si se puede asignar un valor de nulo a un objeto de su tipo. En otras palabras, creo que deberíamos determinar si un tipo de objeto es nulo, no si es Nullable. La diferencia radica en la semántica, es decir, en las razones prácticas para determinar la nulidad, 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, fuentes, etc.), un requisito común es determinar si se puede asignar un nulo al objeto o si el objeto puede contener un nulo. La realización de tales operaciones en tipos que no admiten nulos probablemente producirá errores, generalmente excepciones, que son muy costosas tanto en términos de rendimiento como de requisitos de codificación. Para adoptar el enfoque altamente preferido de evitar dichos problemas de forma proactiva, es necesario determinar si un objeto de un Tipo arbitrario es capaz de contener un nulo; es decir, si generalmente es 'nullable'.

En un sentido 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. En muchos casos, de hecho, 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, se deben realizar pruebas para el concepto general de nulability, frente al concepto de Nullable dependiente de la implementación. Por lo tanto, no deberíamos colgarnos centrándonos únicamente en el tipo .NET Nullable, sino más bien incorporar nuestra comprensión de sus requisitos y comportamiento en el proceso de centrarse en el concepto general y práctico de nulability.

Cuestiones relacionadas