Comment vérifier si un objet est nullable?

203

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.

Autocrate
la source
Le code doit-il inclure des chaînes comme pouvant être annulées? Il s'agit d'un ValueType non générique qui semble être nullable. Ou ne sont-ils pas un ValueType?
TamusJRoyce
La chaîne n'est pas un ValueType. Il s'agit d'un type de référence.
Suncat2000
C'est une très bonne question! Le 'Type.IsNullableType ()' est une sorte de tromperie car il ne vérifie en fait que le type étant un 'Nullable <T>', qui n'a pas renvoyé les résultats attendus si vous vouliez réellement rechercher des types qui peuvent accepter un null valeur (par exemple, j'ai essayé d'utiliser avec a.IsNullableType (), où «a» était un «typeof (chaîne)» déterminé lors de l'exécution)
ErrCode
La réponse est dans fieldInfo.FieldType: vérifiez si FieldType est générique et le type générique est de type Nullable <>. (Exemple: if (FieldType.IsGenericType && FieldType.GetGenericTypeDefinition () == typeof (Nullable <>))). N'essayez pas d'obtenir obj.GetType () il aura UndelyingSystemType de Nullable <T> variable T (dans votre cas de type booléen, au lieu de Nullable <Boolean>), c'est un problème de boxe.
SoLaR

Réponses:

272

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 le objparamètre uniquement pour l'inférence de type générique (pour le rendre facile à appeler) - cela fonctionnerait presque de manière identique sans le objparamètre, cependant.

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

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

Marc Gravell
la source
7
La dernière ligne n'est valide que si vous parvenez à obtenir un Nullable <T> en boîte au lieu de boxer directement à T. C'est possible, mais difficile à réaliser d'après ce dont je me souviens.
Jon Skeet
Ce code m'a été utile, non pas parce que j'avais un Nullable <T> en boîte, mais parce que j'écrivais une classe de base de convertisseur WPF générique et que certaines propriétés sont Nullable, j'ai donc utilisé Nullable.GetUnderlyingType pour détecter ce cas et Activator.CreateInstance à faire une boîte nullable, (Convert.ChangeType ne gère pas nullables btw).
Qwertie
1
@Abel si vous voulez dire sa modification pour clarifier qu'il n'avait pas pris en compte les types de référence, je pense que ma réponse était antérieure à cette modification; le lecteur peut y prendre sa propre décision, en fonction de ses propres besoins, je soupçonne (confirmé: son commentaire sur les ref-types ajouté à 14:42; ma réponse était tout <= 14:34)
Marc Gravell
1
Est-ce que (obj == null) lèvera une exception lorsque obj = 1?
Qi Fan
3
@JustinMorgan Si Test un paramètre générique contraint par T : struct, alors Tn'est pas autorisé à l'être Nullable<>, vous n'avez donc pas besoin de vérification dans ce cas! Je connais le typeNullable<> est une structure, mais en C #, la contrainte where T : structexclut 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."
Jeppe Stig Nielsen
46

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 t) { return false; }
    public static bool IsNullable<T>(T? t) where T : struct { return true; }
}

puis

static void Main(string[] 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
Dean Chalk
la source
7
plus un pour vous monsieur pour l'ajout de cas de test. J'ai utilisé ces cas de test pour vérifier toutes les autres réponses. Plus de gens devraient aller plus loin.
Marty Neal
4
Pour ce que ça vaut, cela ne fonctionne pas dans VB.NET. Il en résulte une erreur de compilation de «la résolution de surcharge a échoué car aucun« IsNullable »accessible n'est le plus spécifique pour ces arguments » dans toutes les situations où Trueserait renvoyé.
ckittel
1
J'aime vraiment cette solution - et c'est dommage que VB ne puisse pas la gérer. J'ai essayé de contourner ValueType, mais j'ai rencontré des problèmes avec le compilateur VB qui n'était pas cohérent quant à la surcharge à utiliser selon qu'elle était appelée en tant que méthode partagée ou extension, j'ai même posé une question à ce sujet car cela semble étrange: stackoverflow.com/ questions / 12319591 /…
James Close
22
Vous vérifiez le type au moment de la compilation , mais il est déjà évident (d'après intellisense) si le type au moment de la compilation est nullable ( System.Nullable<>). Si vous dites object g = e;et ensuite ValueTypeHelper.IsNullable(g), qu'espérez-vous obtenir?
Jeppe Stig Nielsen
18
Je viens de vérifier; cela ne fonctionne pas , comme l'a dit Jeppe. Si les variables sont converties en objet, il retournera toujours false. Vous ne pouvez donc pas déterminer le type d'un objet inconnu au moment de l'exécution de cette façon. La seule fois où cela fonctionne, c'est si le type est fixé au moment de la compilation, et dans ce cas, vous n'avez pas du tout besoin de vérifier l'exécution.
HugoRune
30

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 -ce Nullable<int>un Nullable<>? », mais aussi "est List<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 exemple Nullable<int>, dans la définition de type générique, Nullable<>. Vous pouvez le faire en utilisant la GetGenericTypeDefinition()méthode de la Typeclasse. Vous pouvez ensuite comparer le type résultant à Nullable<>:

Type typeToTest = typeof(Nullable<int>);
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<int>);
bool isList = typeToTest.GetGenericTypeDefinition() == typeof(List<>);
// isList == true

Plusieurs types peuvent sembler identiques, mais un nombre différent d'arguments de type signifie que c'est un type complètement différent.

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

Étant donné que les Typeobjets 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:

var listOfInts = new List<int>();
var listOfStrings = new List<string>();

bool areSameGenericType =
    listOfInts.GetType().GetGenericTypeDefinition() ==
    listOfStrings.GetType().GetGenericTypeDefinition();
// areSameGenericType == true

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:

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

    return typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>);
}
Allon Guralnek
la source
@ AllonGuralnek Il y a une version simplifiée là-bas dans ma réponse. Je voulais le faire en tant que montage et comme ma réputation n'est pas votre niveau, ce serait le montage sans mon nom sur votre réponse, même ainsi, il semble que la critique me tire toujours dans la jambe, qu'elle est un auteur intéressant même si elle était ne pas. Monde étrange, certaines personnes n'ont pas de définitions :).
ipavlu
@ipavlu: Votre version n'est pas simplifiée, elle est en fait plus compliquée. Je pense que vous voulez dire qu'il est optimisé puisque vous mettez en cache le résultat. Cela le rend plus difficile à comprendre.
Allon Guralnek
@ AllonGuralnek classe générique statique et champs initialisés statiques une seule fois, c'est compliqué? Cher Dieu, j'ai commis un crime terrible :).
ipavlu
@ipavku: Oui, car cela n'a rien à voir avec la question "Comment vérifier si un objet est nullable?". J'essaie de rester simple et au point, et j'évite d'introduire des concepts inutiles et sans rapport.
Allon Guralnek
1
@nawfal: Si je vous ai bien compris, votre quête de ma mise en œuvre face à l'existence de 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çant typeof(Nullable<>)par tout autre type). Si vous regardez les sources de GetUnderlyingType()(original ou décompilé), vous verrez qu'il est très similaire à mon code.
Allon Guralnek
30

Cela fonctionne pour moi et semble simple:

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

Pour les types de valeur:

static bool IsNullableValueType<T>(T obj)
{
    return default(T) == null && typeof(T).BaseType != null && "ValueType".Equals(typeof(T).BaseType.Name);
}
Erik
la source
7
Pour ce que ça vaut, c'est aussi le test utilisé par Microsoft
canton7
1
Bien ... N'est-ce pas la meilleure réponse car elle est venue plus tard? Je trouve la réponse du haut si confuse.
Vincent Buscarello
1
Cela devrait être la meilleure réponse. Après des jours d'essais de différentes méthodes, j'ai pensé au hasard à cette solution, je l'ai essayée et elle semble fonctionner parfaitement (par rapport à la réponse la mieux notée)
user3163495
2
C'est une excellente solution pour savoir si une instance peut être définie sur NULL, mais elle retournera true pour tout ce qui peut être défini sur null, y compris les objets ordinaires. Il est important de réaliser que la question d'origine voulait spécifiquement détecter les Nullable ValueTypes.
JamesHoux
20

Eh bien, vous pouvez utiliser:

return !(o is ValueType);

... mais un objet lui-même n'est pas annulable ou autre - un type l' est. Comment envisagiez-vous de l'utiliser?

Jon Skeet
la source
2
Cela m'a un peu découragé. par exemple int? i = 5; typeof (i) renvoie System.Int32 au lieu de Nullable <Int32> - typeof (int?) renvoie Nullable <Int32> .. où puis-je obtenir une certaine clarté sur ce sujet?
Gishu
2
typeof (i) donnera une erreur de compilation - vous ne pouvez pas utiliser typeof avec une variable. Qu'avez-vous réellement fait?
Jon Skeet
15
i.GetType () sera d'abord encadré sur Object, et il n'y a rien de tel qu'un type nullable encadré - Nullable <int> est encadré avec une référence nulle ou un entier encadré.
Jon Skeet
Cette méthode est meilleure que Nullable.GetUnderlyingType (type)! = Null?
Kiquenet
@Kiquenet: Nous n'avons le type ici - juste la valeur.
Jon Skeet
11

La façon la plus simple que je puisse comprendre est:

public bool IsNullable(object obj)
{
    Type t = obj.GetType();
    return t.IsGenericType 
        && t.GetGenericTypeDefinition() == typeof(Nullable<>);
}
CARLOS LOTH
la source
+1. Excellente solution pour les types nullable en boîte. Je n'ai pas encore testé cela spécifiquement. Donc, si quelqu'un d'autre peut vérifier, ce serait apprécié.
TamusJRoyce
Je l'ai déjà testé. J'ai dû créer une sorte de Nullabletype, mais avec une sémantique différente. Dans ma situation, je devrais prendre nullen charge une valeur valide et également ne prendre en charge aucune valeur. Donc, un créé un Optionaltype. Comme il était nécessaire de prendre en charge les nullvaleurs, j'ai également dû implémenter du code pour gérer les Nullablevaleurs dans le cadre de mon implémentation. C'est de là que vient ce code.
CARLOS LOTH
9
Je pense que cette solution est fausse. Le passage d'un type de valeur Nullable en tant qu'argument à une méthode qui attend un paramètre de type object devrait entraîner la boxe. Nullable est un type de valeur et le résultat de la conversion de boxe est un type de référence. Il n'y a pas de nullables encadrés. Je crois que cette méthode retourne toujours faux?
Mishax
1
Un test à ce sujet comme une autre réponse?
Kiquenet
5
Cela ne fonctionne pas en raison de la valeur de boxe. Il retournera toujours FAUX.
N Rocking
10

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:

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

Et voici ma modification du code client-test 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;
    string 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 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:

    public static bool IsObjectNullable<T>(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 à 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é.

Mark Jones
la source
8

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:

public static bool IsNullable(this Type type)
{
    return Nullable.GetUnderlyingType(type) != null;
}

Cela peut alors être appelé ainsi:

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

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

sclarke81
la source
1
Ne vouliez-vous pas utiliser "==" au lieu de "! ="?
vkelman
Bon endroit @vkelman Au lieu de faire ce changement, j'ai mis à jour la réponse pour utiliser la suggestion actuelle de Microsoft car cela a changé depuis que j'ai écrit cela.
sclarke81
6

Attention, lorsque vous boxez un type nullable ( Nullable<int>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))

Il devient un véritable type de référence, vous perdez donc le fait qu'il était nullable.

thinkbeforecoding
la source
3

Peut-être un peu hors sujet, mais encore quelques informations intéressantes. Je trouve beaucoup de gens qui utilisent Nullable.GetUnderlyingType() != nullpour identifier si un type est nullable. Cela fonctionne évidemment, mais Microsoft conseille ce qui suit type.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 ...

/// <summary>Method for testing the performance of several options to determine if a type is     nullable</summary>
[TestMethod]
public void IdentityNullablePerformanceTest()
{
    int attempts = 1000000;

    Type nullableType = typeof(Nullable<int>);

    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();
}
Roel van Megen
la source
1
Salut, il y a probablement un problème avec la mesure du temps, l'assertion peut affecter les résultats. Avez-vous testé sans Assert? Aussi Console.WriteLine doit être en dehors de la zone mesurée. +1 pour tenter de quantifier les problèmes de performances :).
ipavlu
@ipavlu Console.WriteLineest en effet en dehors de la zone mesurée;)
nawfal
Roel, comme l'a mentionné ipavlu, Assertdevrait ê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 pour GetUnderlyingTypeet ~ 1 seconde pour GetGenericTypeDefinition, c'est-à-dire GetGenericTypeDefinitionest deux fois plus rapide (pas trois fois).
nawfal
A fait un autre tour avec 2 nullables et 2 non-nullables - cette fois GetUnderlyingTypeétait 2,5 fois plus lent. Avec seulement des valeurs non annulables - cette fois, les deux sont au coude à coude.
nawfal
Mais plus important encore, GetUnderlyingTypeest 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 comme Activator.CreateInstance(Nullable.GetUnderlyingType(type) ?? type). C'est comme un asmot-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 une GetGenericTypeDefinitionvérification puis obtenir un type générique sera une mauvaise idée. Est également GetUnderlyingTypebeaucoup plus lisible et mémorable. Cela ne me dérangerait pas si je le fais seulement ~ 1000 fois.
nawfal
0

Cette version:

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

:

public static class IsNullable<T>
{
    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<int?>.Result;
ipavlu
la source
Je pense que vous avez répondu vous-même avec cette déclaration statique 'is_nullable'. Astuce: déclarer des objets avec int? (objet a = (int?) 8;) et voyez ce qui se passe.
SoLaR
0

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 Tet Nullable<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:

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

et pour Nullable<T>

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

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.

Lorenz Lo Sauer
la source
0

Je pense que ceux utilisant les tests suggérés par Microsoft IsGenericTypesont bons, mais dans le code pour GetUnderlyingType, Microsoft utilise un test supplémentaire pour s'assurer que vous n'avez pas réussi la définition de type générique Nullable<>:

 public static bool IsNullableType(this Type nullableType) =>
    // instantiated generic type only                
    nullableType.IsGenericType &&
    !nullableType.IsGenericTypeDefinition &&
    Object.ReferenceEquals(nullableType.GetGenericTypeDefinition(), typeof(Nullable<>));
NetMage
la source
-1

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 réussis

    IsNullable_String_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_String_ShouldReturn_True()
    {
        var typ = typeof(string);
        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<bool>);
        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<string, string>);
        var result = typ.IsNullable();
        Assert.IsFalse(result);
    }
VJPPaz
la source