J'ai une méthode générique définie comme ceci:
public void MyMethod<T>(T myArgument)
La première chose que je veux faire est de vérifier si la valeur de myArgument est la valeur par défaut pour ce type, quelque chose comme ceci:
if (myArgument == default(T))
Mais cela ne compile pas car je n'ai pas garanti que T implémentera l'opérateur ==. J'ai donc changé le code en ceci:
if (myArgument.Equals(default(T)))
Maintenant, cela compile, mais échouera si myArgument est nul, ce qui fait partie de ce que je teste. Je peux ajouter une vérification nulle explicite comme ceci:
if (myArgument == null || myArgument.Equals(default(T)))
Maintenant, cela me semble redondant. ReSharper suggère même que je change la partie nulle de myArgument == en myArgument == default (T), là où j'ai commencé. Existe-t-il une meilleure façon de résoudre ce problème?
Je dois prendre en charge les types de références et les types de valeurs.
if (myArgument?.Equals( default(T) ) != null )
.true
dans tous les cas parce queEquals
sera toujours appelé pour les types de valeur carmyArgument
ne peut pas êtrenull
dans ce cas et le résultat deEquals
(un booléen) ne le sera jamaisnull
.Réponses:
Pour éviter la boxe, la meilleure façon de comparer les génériques pour l'égalité est avec
EqualityComparer<T>.Default
. Cela respecteIEquatable<T>
(sans boxe) ainsi queobject.Equals
, et gère toutes lesNullable<T>
nuances "levées". Par conséquent:Cela correspondra à:
Nullable<T>
la source
Person
,p1.Equals(p2)
dépendrait de l'implémentationIEquatable<Person>
sur l'API publique, ou via une implémentation explicite - c'est-à-dire que le compilateur peut voir uneEquals(Person other)
méthode publique . Toutefois; chez les génériques , le même IL est utilisé pour tousT
; unT1
qui arrive à implémenterIEquatable<T1>
doit être traité de manière identique à unT2
qui ne le fait pas - donc non, il ne repérera pas uneEquals(T1 other)
méthode, même si elle existe au moment de l'exécution. Dans les deux cas, il faut aussinull
penser à (l'un ou l'autre objet). Donc, avec des génériques, j'utiliserais le code que j'ai publié.Que dis-tu de ça:
L'utilisation de la
static object.Equals()
méthode vous évite d'avoir à effectuer vous-même lanull
vérification. Ilobject.
n'est probablement pas nécessaire de qualifier explicitement l'appel avec votre contexte, mais je préfixe normalement lesstatic
appels avec le nom du type juste pour rendre le code plus soluble.la source
J'ai pu localiser un article Microsoft Connect qui traite de ce problème en détail:
Voici ce que vous pouvez faire ...
J'ai validé que ces deux méthodes fonctionnent pour une comparaison générique des types de référence et de valeur:
ou
Pour faire des comparaisons avec l'opérateur "==", vous devrez utiliser l'une de ces méthodes:
Si tous les cas de T dérivent d'une classe de base connue, vous pouvez informer le compilateur en utilisant des restrictions de type génériques.
Le compilateur reconnaît alors comment effectuer des opérations sur
MyBase
et ne lancera pas l'erreur "Operator '==' ne peut pas être appliquée aux opérandes de type" T "et" T "" que vous voyez maintenant.Une autre option serait de restreindre T à tout type qui implémente
IComparable
.Et puis utilisez la
CompareTo
méthode définie par l' interface IComparable .la source
Essaye ça:
qui devrait compiler et faire ce que vous voulez.
la source
Equals
méthode deIEqualityComparer
prend deux arguments, les deux objets à comparer, donc non, ce n'est pas redondant.(Édité)
Marc Gravell a la meilleure réponse, mais je voulais publier un simple extrait de code que j'ai élaboré pour le démontrer. Exécutez simplement ceci dans une simple application console C #:
Une dernière chose: quelqu'un avec VS2008 peut-il essayer cela comme méthode d'extension? Je suis coincé avec 2005 ici et je suis curieux de voir si cela serait autorisé.
Edit: Voici comment le faire fonctionner comme méthode d'extension:
la source
Pour gérer tous les types de T, y compris lorsque T est un type primitif, vous devrez compiler dans les deux méthodes de comparaison:
la source
Il va y avoir un problème ici -
Si vous allez autoriser cela à fonctionner pour n'importe quel type, la valeur par défaut (T) sera toujours nulle pour les types de référence et 0 (ou structure pleine de 0) pour les types de valeur.
Ce n'est probablement pas le comportement que vous recherchez, cependant. Si vous souhaitez que cela fonctionne de manière générique, vous devrez probablement utiliser la réflexion pour vérifier le type de T et gérer les types de valeur différents des types de référence.
Alternativement, vous pouvez mettre une contrainte d'interface sur cela, et l'interface pourrait fournir un moyen de vérifier la valeur par défaut de la classe / struct.
la source
Je pense que vous devez probablement diviser cette logique en deux parties et vérifier d'abord null.
Dans la méthode IsNull, nous nous appuyons sur le fait que les objets ValueType ne peuvent pas être null par définition, donc si value se trouve être une classe qui dérive de ValueType, nous savons déjà que ce n'est pas null. D'un autre côté, s'il ne s'agit pas d'un type de valeur, nous pouvons simplement comparer la valeur cast à un objet par rapport à null. Nous pourrions éviter la vérification par rapport à ValueType en allant directement à un transtypage en objet, mais cela signifierait qu'un type de valeur serait encadré, ce que nous voulons probablement éviter car cela implique qu'un nouvel objet est créé sur le tas.
Dans la méthode IsNullOrEmpty, nous vérifions le cas particulier d'une chaîne. Pour tous les autres types, nous comparons la valeur (qui sait déjà n'est pas nulle) avec sa valeur par défaut qui pour tous les types de référence est nulle et pour les types de valeur est généralement une certaine forme de zéro (si elles sont intégrales).
En utilisant ces méthodes, le code suivant se comporte comme vous pouvez vous y attendre:
la source
Méthode d'extension basée sur la réponse acceptée.
Usage:
Alterner avec null pour simplifier:
Usage:
la source
J'utilise:
la source
Je ne sais pas si cela fonctionne avec vos besoins ou non, mais vous pouvez contraindre T à être un type qui implémente une interface telle que IComparable, puis utiliser la méthode ComparesTo () à partir de cette interface (que l'IIRC prend en charge / gère les valeurs nulles) comme ceci :
Il existe probablement d'autres interfaces que vous pourriez également utiliser IEquitable, etc.
la source
@ilitirit:
L'opérateur '==' ne peut pas être appliqué aux opérandes de type 'T' et 'T'
Je ne peux pas penser à un moyen de le faire sans le test null explicite suivi par l'invocation de la méthode Equals ou de object.Equals comme suggéré ci-dessus.
Vous pouvez concevoir une solution en utilisant System.Comparison, mais cela va finir avec beaucoup plus de lignes de code et augmenter considérablement la complexité.
la source
Je pense que tu étais proche.
Maintenant, cela compile, mais échouera si
myArgument
est nul, ce qui fait partie de ce que je teste. Je peux ajouter une vérification nulle explicite comme ceci:Vous avez juste besoin d'inverser l'objet sur lequel l'égal est appelé pour une approche élégante sans danger.
la source