2008-09-20 5 views
65

Encore une fois l'un de ceux-ci: "Y a-t-il une manière intégrée plus facile de faire les choses au lieu de ma méthode d'aide?"Comment puis-je convertir un System.Type en sa version nullable?

Il est donc facile d'obtenir le type sous-jacent à partir d'un type nullable, mais comment obtenir la version nullable d'un type .NET?

J'ai

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

et je veux

int? 
DateTime? 

ou

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

Y at-il une méthode intégrée?

Répondre

91

Voici le code que j'utilise:

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

Nice! C'est une solution vraiment soignée. – ljs

+0

Bonne réponse! Exactement ce que je cherchais. –

+0

Solution fraîche. Pourquoi ne pas en faire une méthode d'extension sur Type lui-même? Cela semble approprié dans cette situation. –

2

Il n'y a rien dans construit que je connais, comme int?, etc. est juste du sucre syntaxique pour Nullable<T>; et ne reçoit pas de traitement spécial au-delà de cela. C'est particulièrement improbable étant donné que vous essayez d'obtenir ceci à partir des informations de type d'un type donné. En règle générale, cela nécessite toujours un certain code 'roll your own'. Vous devez utiliser Reflection pour créer un nouveau type Nullable avec le paramètre type du type d'entrée.

Edit: Comme les commentaires suggèrent effectivement Nullable<>est traité spécialement et dans le temps d'exécution pour démarrer comme expliqué dans this article.

+0

En règle générale, je suis à peu près sûr que le CLR a un peu de magie spéciale pour gérer Nullable <> de façon quelque peu différente. Je vais devoir vérifier ça. – TraumaPony

+0

Je serais intéressé par cela, heureux d'admettre que je me trompe si c'est le cas :-) – ljs

+0

Le CLR a une gestion spéciale pour Nullables. http://blogs.msdn.com/somasegar/archive/2005/08/11/450640.aspx –

7

La réponse de Lyman est géniale et m'a aidé, cependant, il y a encore un bug qui doit être corrigé.

Nullable.GetUnderlyingType(type) ne doit être appelé que si le type n'est pas déjà un type Nullable. Sinon, il semble retourner par erreur le type null lorsque le type dérive de System.RuntimeType (comme quand je passe en typeof(System.Int32)). La version ci-dessous évite de devoir appeler Nullable.GetUnderlyingType(type) en vérifiant si le type est Nullable à la place.

Ci-dessous vous trouverez une version ExtensionMethod de cette méthode qui retourne immédiatement le type à moins c'est un ValueType qui est pas déjà 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); 
} 

(Je suis désolé, mais je ne pouvais pas simplement poster un commentaire à la réponse de Lyman parce que j'était nouveau et ne pas avoir assez de représentant encore.)

14

J'ai deux ou trois méthodes que je J'ai écrit dans ma bibliothèque utilitaire sur laquelle j'ai beaucoup compté. Le premier est un procédé qui permet de convertir tout type de sa forme Nullable <Type> correspondant:

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

La deuxième méthode signale simplement de savoir si un type donné peut être nulle.Cette méthode est appelée par le premier et est utile séparément:

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

La mise en œuvre ci-dessus IsTypeNullable fonctionne comme un champion à chaque fois, mais il est un peu bavard et lent dans sa dernière ligne de code. Le corps de code suivant est le même que ci-dessus pour IsTypeNullable, à l'exception de la dernière ligne de code est plus simple et plus rapide:

 // 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!

Marque

P.S. - A propos de "nullability"

Je devrais répéter une déclaration sur la nullabilité que j'ai faite dans un post séparé, qui s'applique directement à aborder correctement ce sujet. C'est-à-dire, je crois que l'objet de la discussion ici ne devrait pas être comment vérifier pour voir si un objet est un type Nullable générique, mais plutôt si on peut assigner une valeur de null à un objet de son type. En d'autres termes, je pense que nous devrions déterminer si un type d'objet est nullable, pas s'il est nul. La différence est dans la sémantique, à savoir les raisons pratiques pour déterminer la nullité, qui est généralement tout ce qui compte. Dans un système utilisant des objets dont les types sont inconnus avant l'exécution (services Web, appels distants, bases de données, flux, etc.), il est généralement nécessaire de déterminer si une valeur null peut être affectée à l'objet ou si l'objet peut contenir une valeur nulle. Effectuer de telles opérations sur des types non nullables produira probablement des erreurs, généralement des exceptions, qui sont très coûteuses à la fois en termes de performances et de codage. Pour prendre l'approche hautement préférée d'éviter proactivement de tels problèmes, il est nécessaire de déterminer si un objet d'un type arbitraire est capable de contenir un nul; c'est-à-dire, si elle est généralement 'nulle'.

Dans un sens très pratique et typique, la nullité dans les termes .NET n'implique pas nécessairement que le type d'un objet est une forme de Nullable. Dans de nombreux cas, en effet, les objets ont des types de référence, peuvent contenir une valeur nulle et sont donc tous nullables; aucun d'entre eux n'a un type nul. Par conséquent, à des fins pratiques dans la plupart des scénarios, des tests doivent être effectués pour le concept général de nullité, par rapport au concept de Nullable dépendant de la mise en œuvre. Nous ne devrions donc pas nous arrêter en nous concentrant uniquement sur le type .NET Nullable, mais plutôt intégrer notre compréhension de ses exigences et de son comportement dans le processus de focalisation sur le concept général et pratique de la nullité.

Questions connexes