Comment vérifier si un objet donné est nullable en d'autres termes comment implémenter la méthode suivante ...
bool IsNullableValueType(object o)
{
...
}
EDIT: Je recherche des types de valeurs nullables. Je n'avais pas en tête les types de références.
//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 objet de type bool
( System.Boolean
) avec une valeur égale à true
. Ce que je voulais vraiment, c'était un objet de typeNullable<bool>
Alors maintenant, en guise de solution, j'ai décidé de vérifier si o est nullable et de créer un wrapper nullable autour de obj.
Réponses:
Il existe deux types de nullable -
Nullable<T>
et de type référence.Jon m'a corrigé qu'il est difficile d'obtenir un type si il est en boîte, mais vous pouvez le faire avec des génériques: - alors qu'en est-il ci-dessous. Il s'agit en fait de tester le type
T
, mais en utilisant leobj
paramètre uniquement pour l'inférence de type générique (pour le rendre facile à appeler) - cela fonctionnerait presque de manière identique sans leobj
paramètre, cependant.Mais cela ne fonctionnera pas si bien si vous avez déjà encadré la valeur dans une variable d'objet.
Documentation Microsoft: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/nullable-types/how-to-identify-a-nullable-type
la source
T
est un paramètre générique contraint parT : struct
, alorsT
n'est pas autorisé à l'êtreNullable<>
, vous n'avez donc pas besoin de vérification dans ce cas! Je connais le typeNullable<>
est une structure, mais en C #, la contraintewhere T : struct
exclut spécifiquement les types de valeur nullables. La spécification dit: "Notez que bien qu'il soit classé comme type de valeur, un type nullable (§4.1.10) ne satisfait pas la contrainte de type de valeur."Il existe une solution très simple utilisant des surcharges de méthode
http://deanchalk.com/is-it-nullable/
extrait:
puis
la source
True
serait renvoyé.System.Nullable<>
). Si vous ditesobject g = e;
et ensuiteValueTypeHelper.IsNullable(g)
, qu'espérez-vous obtenir?La question de "Comment vérifier si un type est nullable?" est en fait « Comment vérifier si un type est
Nullable<>
? », qui peut être généralisé à « Comment vérifier si un type est un type construit d' un certain type générique? », de sorte qu'il répond non seulement à la question « Est -ceNullable<int>
unNullable<>
? », mais aussi "estList<int>
unList<>
?".La plupart des solutions fournies utilisent la
Nullable.GetUnderlyingType()
méthode, qui ne fonctionnera évidemment qu'avec le cas deNullable<>
. Je n'ai pas vu la solution de réflexion générale qui fonctionnera avec n'importe quel type générique, j'ai donc décidé de l'ajouter ici pour la postérité, même si cette question a déjà été répondue il y a longtemps.Pour vérifier si un type est une forme d'
Nullable<>
aide de la réflexion, vous devez d' abord convertir votre type générique construit, par exempleNullable<int>
, dans la définition de type générique,Nullable<>
. Vous pouvez le faire en utilisant laGetGenericTypeDefinition()
méthode de laType
classe. Vous pouvez ensuite comparer le type résultant àNullable<>
:La même chose peut être appliquée à tout type générique:
Plusieurs types peuvent sembler identiques, mais un nombre différent d'arguments de type signifie que c'est un type complètement différent.
Étant donné que les
Type
objets sont instanciés une fois par type, vous pouvez vérifier l'égalité de référence entre eux. Donc, si vous voulez vérifier si deux objets ont la même définition de type générique, vous pouvez écrire:Si vous souhaitez vérifier si un objet est nullable, plutôt qu'un
Type
, alors vous pouvez utiliser la technique ci-dessus avec la solution de Marc Gravell pour créer une méthode assez simple:la source
Nullable.GetUnderlyingType()
celle déjà fournie par le framework. Pourquoi ne pas simplement utiliser la méthode dans le framework? Et bien tu devrais. Il est plus clair, plus concis et mieux testé. Mais dans mon article, j'essaie d'enseigner comment utiliser la réflexion pour obtenir les informations que vous souhaitez, afin que quelqu'un puisse les appliquer à n'importe quel type (en les remplaçanttypeof(Nullable<>)
par tout autre type). Si vous regardez les sources deGetUnderlyingType()
(original ou décompilé), vous verrez qu'il est très similaire à mon code.Cela fonctionne pour moi et semble simple:
Pour les types de valeur:
la source
Eh bien, vous pouvez utiliser:
... mais un objet lui-même n'est pas annulable ou autre - un type l' est. Comment envisagiez-vous de l'utiliser?
la source
La façon la plus simple que je puisse comprendre est:
la source
Nullable
type, mais avec une sémantique différente. Dans ma situation, je devrais prendrenull
en charge une valeur valide et également ne prendre en charge aucune valeur. Donc, un créé unOptional
type. Comme il était nécessaire de prendre en charge lesnull
valeurs, j'ai également dû implémenter du code pour gérer lesNullable
valeurs dans le cadre de mon implémentation. C'est de là que vient ce code.Il y a deux problèmes ici: 1) tester pour voir si un type est nullable; et 2) tester pour voir si un objet représente un type nullable.
Pour le problème 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 (test d'un objet), 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 <T> renvoie toujours false. Étant donné que les types de référence sont intrinsèquement nullables, le test d'un type de référence doit toujours retourner 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:
Et voici ma modification du code client-test pour la solution ci-dessus:
La raison pour laquelle j'ai modifié l'approche de Dean dans IsObjectNullable <T> (T t) est que son approche d'origine renvoyait toujours false pour un type de référence. Puisqu'une méthode comme IsObjectNullable devrait être capable de gérer des valeurs de type référence et puisque tous les types de référence sont intrinsèquement nullables, alors si un type de référence ou un null est passé, 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:
Cependant, le problème avec cette dernière approche à méthode unique est que les performances souffrent lorsqu'un paramètre Nullable <T> est utilisé. Il faut beaucoup plus de temps au processeur pour exécuter la dernière ligne de cette seule méthode que pour permettre au compilateur de choisir la deuxième surcharge de méthode indiquée précédemment lorsqu'un paramètre de type Nullable <T> 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'objet d'origine ou d'une copie exacte, comme indiqué dans les exemples. Cependant, si un objet nullable est encadré dans un autre type (tel qu'un objet, etc.) au lieu de rester 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 d'origine non encadrée 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 s'appuyer sur le test du type de l'objet d'origine, et non sur sa référence (par exemple, le code doit avoir accès au type d'origine de l'objet pour déterminer la nullité). Dans ces cas plus courants, IsTypeNullable (voir lien) est une méthode fiable pour déterminer la nullité.
PS - À 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 pour aborder correctement ce sujet. C'est-à-dire que je pense que l'objectif de la discussion ici ne devrait pas être de savoir comment vérifier si un objet est un type Nullable générique, mais plutôt si l'on peut attribuer une valeur nulle à un objet de son 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 réside 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 jusqu'à l'exécution (services Web, appels distants, bases de données, flux, etc.), une exigence courante consiste à déterminer si une valeur null peut être affectée à l'objet ou si l'objet peut contenir un nul. La réalisation de telles opérations sur des types non annulables entraînera 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 d'éviter proactivement de tels problèmes, il est nécessaire de déterminer si un objet de type arbitraire est capable de contenir un null; c'est-à-dire s'il est généralement «annulable».
Dans un sens très pratique et typique, la nullité en termes .NET n'implique pas du tout nécessairement que le Type d'un objet est une forme de Nullable. Dans de nombreux cas, en fait, 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 Nullable. 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 dépendant de l'implémentation de Nullable. Nous ne devons 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 concentration sur le concept général et pratique de nullité.
la source
La solution la plus simple que j'ai trouvée consiste à implémenter la solution de Microsoft ( Comment: identifier un type nul (Guide de programmation C #) ) comme méthode d'extension:
Cela peut alors être appelé ainsi:
Cela semble également un moyen logique d'accéder
IsNullable()
car il s'intègre à toutes les autresIsXxxx()
méthodes de laType
classe.la source
Attention, lorsque vous boxez un type nullable (
Nullable<int>
ou int? Par exemple):Il devient un véritable type de référence, vous perdez donc le fait qu'il était nullable.
la source
Peut-être un peu hors sujet, mais encore quelques informations intéressantes. Je trouve beaucoup de gens qui utilisent
Nullable.GetUnderlyingType() != null
pour identifier si un type est nullable. Cela fonctionne évidemment, mais Microsoft conseille ce qui suittype.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)
(voir http://msdn.microsoft.com/en-us/library/ms366789.aspx ).J'ai regardé cela d'un point de vue performance. La conclusion du test (un million de tentatives) ci-dessous est que lorsqu'un type est nullable, l'option Microsoft offre les meilleures performances.
Nullable.GetUnderlyingType (): 1335ms (3 fois plus lent)
GetGenericTypeDefinition () == typeof (Nullable <>): 500 ms
Je sais que nous parlons d'un peu de temps, mais tout le monde aime modifier les millisecondes :-)! Donc, si votre patron veut que vous réduisiez quelques millisecondes, c'est votre sauveur ...
la source
Console.WriteLine
est en effet en dehors de la zone mesurée;)Assert
devrait être en dehors de la boucle. Deuxièmement, vous devez également le tester par rapport aux non-nullables pour tester les faux cas. J'ai fait un test similaire (2 nulables et 4 non-nullables) et j'obtiens ~ 2 secondes pourGetUnderlyingType
et ~ 1 seconde pourGetGenericTypeDefinition
, c'est-à-direGetGenericTypeDefinition
est deux fois plus rapide (pas trois fois).GetUnderlyingType
était 2,5 fois plus lent. Avec seulement des valeurs non annulables - cette fois, les deux sont au coude à coude.GetUnderlyingType
est utile lorsque vous devez vérifier la nullité et obtenir le type sous-jacent s'il est nullable. Ceci est très utile et vous voyez souvent des modèles commeActivator.CreateInstance(Nullable.GetUnderlyingType(type) ?? type)
. C'est comme unas
mot-clé, vérifie la distribution ainsi que le résultat et le retour. Si vous voulez récupérer le type sous-jacent de nullable, faire uneGetGenericTypeDefinition
vérification puis obtenir un type générique sera une mauvaise idée. Est égalementGetUnderlyingType
beaucoup plus lisible et mémorable. Cela ne me dérangerait pas si je le fais seulement ~ 1000 fois.Cette version:
:
la source
Voici ce que j'ai trouvé, car tout le reste semblait échouer - au moins sur l' API - Portable Class Library / .NET Core avec> = C # 6
Solution: étendez les méthodes statiques pour tout type
T
etNullable<T>
utilisez le fait que la méthode d'extension statique, correspondant au type sous-jacent, sera invoquée et aura priorité sur le génériqueT
méthode d'extension .Pour
T
:et pour
Nullable<T>
L'utilisation de Reflection et
type.IsGenericType
... n'a pas fonctionné sur mon ensemble actuel de Runtimes .NET. 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.
la source
Je pense que ceux utilisant les tests suggérés par Microsoft
IsGenericType
sont bons, mais dans le code pourGetUnderlyingType
, Microsoft utilise un test supplémentaire pour s'assurer que vous n'avez pas réussi la définition de type génériqueNullable<>
:la source
un moyen simple de le faire:
ce sont mes tests unitaires et tous réussis
tests unitaires réels
la source