Null ou de comparaison par défaut de l'argument générique en C#
J'ai une méthode générique défini 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 veut pas compiler, car je n'ai pas la garantie que T va mettre en œuvre l'opérateur==. Donc j'ai changé le code de cette:
if (myArgument.Equals(default(T)))
Maintenant cette compile, mais échouera si myArgument est nulle, ce qui est une partie de ce que je suis en essais pour. Je peux ajouter une valeur null explicite case comme ceci:
if (myArgument == null || myArgument.Equals(default(T)))
Maintenant c'est redondant pour moi. ReSharper est même ce qui suggère que je change le myArgument == null part dans myArgument == défaut(T) qui est l'endroit où j'ai commencé. Est-il une meilleure façon de résoudre ce problème?
J'ai besoin de soutien deux référence des types et des types de valeur.
- C# supporte maintenant Null Opérateurs Conditionnels, qui est syntatic de sucre pour le dernier exemple que vous donnez. Votre code pourrait devenir
if (myArgument?.Equals( default(T) ) != null )
. - Cela ne fonctionne pas pour les types de valeur, c'est à dire évalue à
true
en tout cas, carEquals
va toujours être appelé pour les types de valeur depuismyArgument
ne peut pas êtrenull
dans ce cas et le résultat deEquals
(un booléen) ne sera jamaisnull
. - Tout aussi précieux, presque en double (donc pas de vote pour fermer): vous ne Pouvez pas l'opérateur == être appliquée à des types génériques en C#?
Vous devez vous connecter pour publier un commentaire.
Pour éviter la boxe, la meilleure façon de comparer les génériques pour l'égalité est avec
EqualityComparer<T>.Default
. Ce respecteIEquatable<T>
(sans la boxe) ainsi queobject.Equals
, et s'occupe de toutes lesNullable<T>
"levé" nuances. Donc:Ce sera un match:
Nullable<T>
T.Equals
trop de respectIEquatable<T>
interface? disonsPerson
class/struct estIEquatable<Person>
. Ne serait-ce pas l'appel dep1.Equals(p2)
appel de la version générique d'abord, et si pas présent, alorsobject.Equals
? N'est-ce pasEqualityComparer<Person>.Default.Equals
trop fait?Person
,p1.Equals(p2)
dépendra de savoir si elle met en œuvreIEquatable<Person>
sur l'API publique, ou par l'intermédiaire de mise en œuvre explicite - c'est à dire le compilateur voir un publicEquals(Person other)
méthode. Cependant, dans génériques, la même IL est utilisé pour tous lesT
; unT1
qui arrive à mettre en œuvreIEquatable<T1>
doit être traitée de façon identique à uneT2
qui n'est pas si non, c' ne spot unEquals(T1 other)
méthode, même si elle existe au moment de l'exécution. Dans les deux cas, il y a aussinull
à penser (de l'objet). Donc, avec les génériques, je voudrais utiliser le code que j'ai posté.Object.Equals
sera la cause de la boxeobject
(ou d'une interface, commeIEquatable
); cela inclut la transmission des types de valeur en tant que paramètres de typeobject
, qui est ce queobject.Equals
a. Lorsque vous effectuez cette opération, la valeur est "encadré" dans un objet, ce qui signifie: une nouvelle géré tas d'objet est alloué qui encapsule une copie de la valeur. Lorsque vous de la boîte de deux entiers: personne ne s'en occupe. Mais quand c'est dans une chaude-chemin de la boucle, vous pouvez allouer beaucoup, très très rapidement - mauvais.object.Equals(valueX, valueY)
, vous avez alloué 2 nouveaux objets sur le tas managé; multiplier par combien de fois vous appelez la méthode, et vous pourrait ont un réel GC problèmevalue == default
etvalue == default(T)
se comportent différemment en C# 7.1+ dans le contexte de génériques.T
's valeur égale à la chaîne.est-il vide?null
, de sorte qu'il sera un test pournull
, pas""
Comment à ce sujet:
À l'aide de la
static object.Equals()
méthode évite la nécessité pour vous de faire lenull
vérifier vous-même. Explicitement la qualification de l'appel avecobject.
n'est probablement pas nécessaire en fonction de votre contexte, mais j'ai l'habitude de préfixestatic
des appels avec le type de nom, juste pour rendre le code plus soluble.J'ai été capable de localiser un Microsoft se Connecter à l'article qui traite de cette question dans le détail:
Ici est ce que vous pouvez faire...
J'ai validé le fait que ces deux méthodes de travail pour un générique de comparaison de référence et des types de valeur:
ou
De faire des comparaisons avec le "==" l'opérateur, vous aurez besoin d'utiliser une de ces méthodes:
Si tous les cas de T dériver à partir d'une classe de base, vous pouvez laisser le compilateur d'aide de type générique restrictions.
Le compilateur reconnaît alors comment faire pour effectuer des opérations sur les
MyBase
et de ne pas jeter le "Opérateur '==' ne peut pas être appliquée à des opérandes de type 'T' et 'T'" erreur que vous voyez maintenant.Une autre option serait de restreindre T de n'importe quel type qui implémente
IComparable
.Et ensuite utiliser le
CompareTo
méthode définie par la Interface IComparable.Essayez ceci:
qui devrait compiler et faire ce que vous voulez.
Equals
méthode deIEqualityComparer
prend deux arguments, les deux objets à comparer, donc non, il n'est pas redondant.(Édité)
Marc Gravel a la meilleure réponse, mais je voulais poster un simple fragment de code suivant, j'ai travaillé jusqu'à le démontrer. Lancez-le dans une simple console C# app:
Une chose: quelqu'un peut-il avec VS2008 essayer cela comme une extension de la méthode? Je suis coincé ici à 2005 et je suis curieux de voir si cela serait autorisé.
Edit: Ici est de savoir comment le faire fonctionner comme une extension de la méthode:
À gérer tous les types de T, y compris où T est un type primitif, vous aurez besoin de compiler dans les deux méthodes de comparaison:
Il va y avoir un problème ici -
Si vous allez permettre que cela fonctionne pour n'importe quel type, par défaut(T) sera toujours null pour les types référence, et 0 (ou structure complète de 0) pour les types de valeur.
Ce n'est probablement pas le comportement que vous êtes après, si. Si vous voulez que cela fonctionne de manière générique, vous avez probablement besoin d'utiliser la réflexion pour vérifier le type de T, et la poignée de la valeur des types différents de types de référence.
Alternativement, vous pouvez mettre une contrainte d'interface sur ce point, et l'interface pourrait offrir un moyen de vérifier que le défaut de la class/struct.
Je pense que vous avez probablement besoin de diviser cette logique en deux parties et de vérifier la valeur null en premier.
Dans la méthode IsNull, nous sommes en s'appuyant sur le fait que ValueType les objets ne peut pas être nulle par définition donc, si la valeur se trouve être une classe qui dérive de ValueType, nous savons déjà ce n'est pas null. D'autre part, si ce n'est pas un type de valeur alors il nous suffit de comparer la valeur de fonte d'un objet par rapport à la valeur null. Nous pourrions éviter le contre-vérifier les ValueType en allant directement à un casting pour objet, mais cela voudrait dire qu'une valeur de type obtiendrait en boîte qui est probablement quelque chose que nous voulons éviter, puisqu'elle implique qu'un nouvel objet est créé sur le tas.
Dans le IsNullOrEmpty méthode, nous sommes de contrôle pour le cas particulier d'une chaîne de caractères. Pour tous les autres types, nous comparons la valeur (qui savent déjà est pas null) contre c'est la valeur par défaut 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 ils sont partie intégrante).
À l'aide de ces méthodes, le code suivant se comporte comme vous vous en doutez:
J'utilise:
Ne sais pas si cela fonctionne avec votre besoin ou pas, mais vous pourrait contraindre T être d'un Type qui implémente une interface comme IComparable et ensuite utiliser le ComparesTo() la méthode de l'interface (qui IIRC/supports de poignées null) comme ceci:
Il y a probablement d'autres interfaces que vous pouvez utiliser ainsi IEquitable, etc.
@ilitirit:
Opérateur '==' ne peut pas être appliquée à des opérandes de type 'T' et 'T'
Je ne peux pas penser à un moyen de le faire sans l'explicite null test suivi par l'invocation de la méthode Equals ou d'un objet.Égal comme suggéré ci-dessus.
Vous pouvez concevoir une solution en utilisant le Système.La comparaison, mais vraiment c'est la fin avec beaucoup plus de lignes de code et d'augmenter considérablement la complexité.
Je pense que vous étiez proche.
Maintenant cette compile, mais échouera si
myArgument
est nulle, ce qui est une partie de ce que je suis en essais pour. Je peux ajouter une valeur null explicite case comme ceci:Vous avez juste besoin d'inverser l'objet sur lequel l'égal est appelé pour un élégant null approche sécuritaire.