81 votes

Comment convertir un System.Type en sa version nullable ?

Une fois de plus, un de ceux-là : "Y a-t-il une façon plus simple de faire les choses à la place de ma méthode d'aide ?"

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

J'ai donc

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

Existe-t-il une méthode intégrée ?

117voto

Alex Lyman Points 7480

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

Edit : Le code original avait un bug où il se comportait de manière inattendue si type était lui-même un Nullable<T> .

17voto

Mark Jones Points 738

J'ai quelques méthodes que j'ai écrites dans ma bibliothèque d'utilitaires et sur lesquelles je me suis beaucoup appuyé. La première est une méthode qui convertit n'importe quel Type en sa forme Nullable<Type> correspondante :

    /// <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 indique simplement si un Type donné est nullable. Cette méthode est appelée par la première 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<>);
    }

L'implémentation de IsTypeNullable ci-dessus fonctionne comme un champion à chaque fois, mais elle est légèrement verbeuse et lente dans sa dernière ligne de code. Le corps de code suivant est le même que celui de IsTypeNullable ci-dessus, sauf que 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;

Profitez-en !

Mark

P.S. - À propos de la "nullité"

Je dois répéter une déclaration sur la nullité que j'ai faite dans un autre message, qui s'applique directement à la façon d'aborder correctement ce sujet. En d'autres termes, je pense que la discussion ne devrait pas porter sur la manière de vérifier si un objet est un type générique Nullable, mais plutôt sur la question de savoir si l'on peut attribuer la valeur null à un objet de ce type. En d'autres termes, je pense que nous devrions déterminer si un type d'objet est nullable, et non s'il est Nullable. La différence se situe au niveau de la sémantique, à savoir les raisons pratiques de déterminer la nullité, ce qui est généralement tout ce qui compte.

Dans un système utilisant des objets dont les types peuvent être inconnus jusqu'au moment de l'exécution (services web, appels à distance, bases de données, flux, etc.), une exigence commune est de déterminer si un null peut être assigné à l'objet, ou si l'objet peut contenir un null. L'exécution de telles opérations sur des types non nuls produira probablement des erreurs, généralement des exceptions, qui sont très coûteuses en termes de performances et d'exigences de codage. Pour éviter de tels problèmes de manière proactive, il est nécessaire de déterminer si un objet d'un type arbitraire est capable de contenir un null, c'est-à-dire s'il est généralement "nullable".

Dans un sens très pratique et typique, la nullité en termes .NET n'implique pas du tout que le Type d'un objet soit une forme de Nullable. En fait, dans de nombreux cas, les objets ont des types de référence, peuvent contenir une valeur nulle et sont donc tous annulables ; aucun d'entre eux n'a un type Nullable. Par conséquent, pour des raisons pratiques, dans la plupart des scénarios, les tests doivent être effectués pour le concept général de nullité, plutôt que pour le concept de Nullable qui dépend de l'implémentation. Il ne faut donc pas se focaliser uniquement sur le type Nullable de .NET, 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 nullité.

9voto

Thracx Points 76

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

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

Vous trouverez ci-dessous une version ExtensionMethod de cette méthode qui retournera immédiatement le type sauf si il s'agit d'un ValueType qui n'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 je suis nouveau et que je n'ai pas encore assez de représentants).

2voto

ljs Points 16511

Il n'y a rien d'intégré à ma connaissance, car les int ?, etc. ne sont que du sucre syntaxique pour Nullable<T> et ne bénéficient pas d'un traitement spécial au-delà. C'est d'autant plus improbable que vous essayez d'obtenir ceci à partir des informations de type d'un type donné. En général, cela nécessite toujours un peu de code de type "roll your own". Vous devriez utiliser Reflection pour créer un nouveau type Nullable avec le paramètre de type du type d'entrée.

Edit : Comme les commentaires le suggèrent, en fait Nullable<> est traitées de manière spéciale, et dans le runtime pour démarrer comme expliqué dans le document cet article .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X