Comment vérifier si un object est nullable?

Comment vérifier si un object donné est nullable en d’autres termes comment implémenter la méthode suivante …

bool IsNullableValueType(object o) { ... } 

EDIT: Je recherche des types de valeur nullable. Je ne pensais pas aux types de ref.

 //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 fait maintenant référence à un object de type bool ( System.Boolean ) avec une valeur égale à true . Ce que je voulais vraiment était un object de type Nullable

Alors maintenant, en tant que travail, j’ai décidé de vérifier si o est nullable et de créer un wrapper nullable autour d’Obj.

Il existe deux types de NullableNullable et de référence-type.

Jon m’a corrigé le fait qu’il est difficile de saisir le texte s’il est encadré, mais vous pouvez le faire avec les génériques: Ceci est en train de tester le type T , mais en utilisant le paramètre obj uniquement pour l’inférence de type générique (pour le rendre facile à appeler) – cela fonctionnerait presque de manière identique sans le paramètre obj , cependant.

 static bool IsNullable(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 return false; // value-type } 

Mais cela ne fonctionnera pas si vous avez déjà inséré la valeur dans une variable d’object.

Il existe une solution très simple utilisant des surcharges de méthode

http://deanchalk.com/is-it-nullable/

extrait:

 public static class ValueTypeHelper { public static bool IsNullable(T t) { return false; } public static bool IsNullable(T? t) where T : struct { return true; } } 

puis

 static void Main(ssortingng[] 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 

La question de “Comment vérifier si un type est nullable?” est en fait “Comment vérifier si un type est Nullable<> ?”, ce qui peut être généralisé à “Comment vérifier si un type est un type construit de type générique?”, de sorte qu’il ne réponde pas seulement à la question “Est Nullable un Nullable<> ? “, mais aussi” Est-ce que List une List<> ? “.

La plupart de la solution fournie utilise la méthode Nullable.GetUnderlyingType() , qui ne fonctionnera évidemment que dans le cas de Nullable<> . Je n’ai pas vu la solution de reflection générale qui fonctionnera avec n’importe quel type générique, alors j’ai décidé de l’append ici pour la postérité, même si cette question a déjà reçu une réponse il y a longtemps.

Pour vérifier si un type est une forme de Nullable<> utilisant la reflection, vous devez d’abord convertir votre type générique construit, par exemple Nullable , en une définition de type générique, Nullable<> . Vous pouvez le faire en utilisant la méthode GetGenericTypeDefinition() de la classe Type . Vous pouvez ensuite comparer le type résultant à Nullable<> :

 Type typeToTest = typeof(Nullable); bool isNullable = typeToTest.GetGenericTypeDefinition() == typeof(Nullable<>); // isNullable == true 

La même chose peut être appliquée à tout type générique:

 Type typeToTest = typeof(List); bool isList = typeToTest.GetGenericTypeDefinition() == typeof(List<>); // isList == true 

Plusieurs types peuvent sembler identiques, mais un nombre différent d’arguments de type signifie qu’il s’agit d’un type complètement différent.

 Type typeToTest = typeof(Action); bool isAction1 = typeToTest.GetGenericTypeDefinition() == typeof(Action<>); bool isAction2 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,>); bool isAction3 = typeToTest.GetGenericTypeDefinition() == typeof(Action<,,>); // isAction1 == false // isAction2 == true // isAction3 == false 

Puisque l’object Type est instancié une fois par type, vous pouvez vérifier l’égalité de référence entre eux. Donc, si vous voulez vérifier si deux objects ont la même définition de type générique, vous pouvez écrire:

 var listOfInts = new List(); var listOfSsortingngs = new List(); bool areSameGenericType = listOfInts.GetType().GetGenericTypeDefinition() == listOfSsortingngs.GetType().GetGenericTypeDefinition(); // areSameGenericType == true 

Si vous souhaitez vérifier si un object est nullable, plutôt qu’un Type , vous pouvez utiliser la technique ci-dessus avec la solution de Marc Gravell pour créer une méthode assez simple:

 static bool IsNullable(T obj) { if (!typeof(T).IsGenericType) return false; return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>); } 

Eh bien, vous pouvez utiliser:

 return !(o is ValueType); 

… mais un object lui-même n’est pas nullable ou autrement – un type est. Comment aviez-vous l’intention d’utiliser cela?

Cela fonctionne pour moi et semble simple:

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

La façon la plus simple de comprendre est la suivante:

 public bool IsNullable(object obj) { Type t = obj.GetType(); return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>); } 

Il y a deux problèmes ici: 1) tester pour voir si un type est nullable; et 2) tester pour voir si un object représente un type nullable.

Pour le numéro 1 (test d’un type), voici une solution que j’ai utilisée dans mes propres systèmes: Solution TypeIsNullable-check

Pour le problème 2 (tester un object), la solution de Dean Chalk ci-dessus fonctionne pour les types de valeur, mais elle ne fonctionne pas pour les types de référence, car l’utilisation de la surcharge renvoie toujours false. Comme les types de référence sont insortingnsèquement nullables, tester un type de référence doit toujours renvoyer true. Veuillez consulter la note [À propos de la “nullité”] ci-dessous pour une explication de ces sémantiques. Voici donc ma modification de l’approche de Dean:

  public static bool IsObjectNullable(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? obj) where T : struct { // Always return true, since the object-type passed is guaranteed by the comstackr to always be nullable return true; } 

Et voici ma modification du code de test client pour la solution ci-dessus:

  int a = 123; int? b = null; object c = new object(); object d = null; int? e = 456; var f = (int?)789; ssortingng 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 raison pour laquelle j’ai modifié l’approche de Dean dans IsObjectNullable (T t) est que son approche d’origine renvoyait toujours false pour un type de référence. Étant donné qu’une méthode comme IsObjectNullable devrait être capable de gérer les valeurs de type référence et que tous les types de référence sont insortingnsèquement nullables, alors, si un type de référence ou un null est transmis, la méthode doit toujours retourner true.

Les deux méthodes ci-dessus peuvent être remplacées par la méthode unique suivante et obtenir le même résultat:

  public static bool IsObjectNullable(T obj) { Type argType = typeof(T); if (!argType.IsValueType || obj == null) return true; return argType.IsGenericType && argType.GetGenericTypeDefinition() == typeof(Nullable<>); } 

Cependant, le problème avec cette dernière approche de méthode unique est que les performances en pâtissent lorsqu’un paramètre Nullable est utilisé. Il faut beaucoup plus de temps au processeur pour exécuter la dernière ligne de cette méthode unique que pour permettre au compilateur de choisir la seconde surcharge de méthode affichée précédemment lorsqu’un paramètre de type Nullable est utilisé dans l’appel IsObjectNullable. Par conséquent, la solution optimale consiste à utiliser l’approche à deux méthodes illustrée ici.

CAVEAT: Cette méthode ne fonctionne de manière fiable que si elle est appelée à l’aide de la référence d’object d’origine ou d’une copie exacte, comme indiqué dans les exemples. Toutefois, si un object nullable est encadré par un autre type (tel qu’un object, etc.) au lieu de restr dans sa forme Nullable <> d’origine, cette méthode ne fonctionnera pas de manière fiable. Si le code appelant cette méthode n’utilise pas la référence d’objet unboxed d’origine ou une copie exacte, il ne peut pas déterminer de manière fiable la nullité de l’objet à l’aide de cette méthode.

Dans la plupart des scénarios de codage, pour déterminer la nullité, il faut plutôt tester le type de l’object d’origine, et non sa référence (par exemple, le code doit avoir access au type d’origine de l’object pour déterminer la nullité). Dans ces cas plus courants, IsTypeNullable (voir lien) est une méthode fiable pour déterminer la nullité.

PS – A propos de la “nullité”

Je devrais répéter une déclaration sur la nullité que j’ai faite dans un article séparé, qui s’applique directement à traiter correctement ce sujet. En d’autres termes, je pense que la discussion ne doit pas porter sur la manière de vérifier si un object est un type Nullable générique, mais plutôt sur la possibilité d’atsortingbuer une valeur null à un object de ce type. En d’autres termes, je pense que nous devrions déterminer si un type d’object est nullable, et non s’il est nullable. La différence réside dans la sémantique, à savoir les raisons pratiques pour déterminer la nullité, ce qui est généralement tout ce qui compte.

Dans un système utilisant des objects dont les types sont peut-être inconnus jusqu’à l’exécution (services Web, appels distants, bases de données, stream, etc.), il est souvent nécessaire de déterminer si une valeur NULL peut être affectée à l’object ou s’il peut contenir un null. 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 en termes de performances et d’exigences de codage. Pour adopter l’approche hautement préférée consistant à éviter de tels problèmes de manière proactive, il est nécessaire de déterminer si un object d’un Type arbitraire est capable de contenir un null; c’est-à-dire si elle est généralement “nullable”.

Dans un sens très pratique et typique, la nullité dans les termes .NET n’implique pas nécessairement que le type d’un object est une forme de Nullable. Dans de nombreux cas, les objects ont des types de référence, peuvent contenir une valeur nulle et sont donc tous nuls; aucun d’entre eux n’a un type Nullable. Par conséquent, pour des raisons pratiques dans la plupart des scénarios, des tests devraient être effectués pour le concept général de nullité, par opposition au concept de Nullable dépendant de l’implémentation. Nous ne devrions donc pas nous attarder en nous concentrant uniquement sur le type .NET Nullable, mais en incorporant plutôt notre compréhension de ses exigences et de son comportement dans le processus de focalisation sur le concept pratique général de la nullité.

Attention, lors de la mise en boîte d’un type Nullable ( Nullable ou int? Par exemple):

 int? nullValue = null; object boxedNullValue = (object)nullValue; Debug.Assert(boxedNullValue == null); int? value = 10; object boxedValue = (object)value; Debug.Assert( boxedValue.GetType() == typeof(int)) 

Cela devient un vrai type de référence, donc vous perdez le fait que c’était nullable.

La solution la plus simple consiste à implémenter la solution de Microsoft ( Comment: identifier un type Nullable (Guide de programmation C #) ) en tant que méthode d’extension:

 public static bool IsNullable(this Type type) { return type.IsGenericType && type.GetGenericTypeDefinition() != typeof(Nullable<>); } 

Cela peut alors être appelé comme ça:

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

Cela semble également être un moyen logique d’accéder à IsNullable() car il s’intègre à toutes les autres méthodes IsXxxx() de la classe Type .

Peut-être un peu hors sujet, mais encore quelques informations intéressantes. Je trouve que beaucoup de personnes utilisent Nullable.GetUnderlyingType() != null pour identifier si un type est nullable. Cela fonctionne évidemment, mais Microsoft conseille le type type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) (voir http://msdn.microsoft.com/en-us/library/ms366789.aspx ).

J’ai regardé cela d’un sharepoint vue performance. La conclusion du test (un million de tentatives) ci-dessous est que, lorsqu’un type est un fichier nullable, l’option Microsoft offre les meilleures performances.

Nullable.GetUnderlyingType (): 1335ms (3 fois plus lent)

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

Je sais que nous parlons d’un peu de temps, mais tout le monde aime peaufiner les millisecondes :-)! Donc, si votre patron veut que vous réduisiez quelques millisecondes, alors c’est votre sauveur …

 /// Method for testing the performance of several options to determine if a type is nullable [TestMethod] public void IdentityNullablePerformanceTest() { int attempts = 1000000; Type nullableType = typeof(Nullable); 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(); } 

Cette version:

  • la mise en cache des résultats est plus rapide,
  • ne nécessite pas de variables inutiles, comme Method (T obj)
  • PAS COMPLIQUÉ :),
  • juste classe générique statique, qui a des champs calculés une fois

:

 public static class IsNullable { 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.Result; 

Voici ce que je suis venu avec, comme tout le rest semblait échouer – au moins sur le PLC – Portable Class Library / .NET Core avec> = C # 6

Solution: Étendre les méthodes statiques pour tout type T et Nullable et utiliser le fait que la méthode d’extension statique, correspondant au type sous-jacent, va être invoquée et a priorité sur la méthode d’extension T générique.

Pour T :

 public static partial class ObjectExtension { public static bool IsNullable(this T self) { return false; } } 

et pour Nullable

 public static partial class NullableExtension { public static bool IsNullable(this Nullable self) where T : struct { return true; } } 

L’utilisation de Reflection et de type.IsGenericType … ne fonctionnait pas sur mon ensemble actuel de .NET Runtimes. La documentation MSDN n’a pas non plus aidé.

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

En partie parce que l’API Reflection a été modifiée de manière assez significative dans .NET Core.

un moyen simple de le faire:

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

ce sont mes tests unitaires et tous passés

  IsNullable_Ssortingng_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 

tests unitaires réels

  [TestMethod] public void IsNullable_Ssortingng_ShouldReturn_True() { var typ = typeof(ssortingng); 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); 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); var result = typ.IsNullable(); Assert.IsFalse(result); }