web-dev-qa-db-fra.com

Pourquoi ne puis-je pas comparer un KeyValuePair <TKey, TValue> avec la valeur par défaut

Dans .Net 2.5, je peux généralement obtenir une comparaison d'égalité (==) entre une valeur et son type par défaut

if (myString == default(string))

Cependant, j'obtiens l'exception suivante lorsque j'essaie d'exécuter une comparaison d'égalité sur un KeyValuePair par défaut et un KeyValuePair

Échantillon de code (à partir d'une méthode de pré-extension, classe ListUtilities statique proto-lambda :))

public static TKey 
        FirstKeyOrDefault<TKey, TValue>(Dictionary<TKey, TValue> lookups, 
                   Predicate<KeyValuePair<TKey, TValue>> predicate)
{
    KeyValuePair<TKey, TValue> pair = FirstOrDefault(lookups, predicate);

    return pair == default(KeyValuePair<TKey, TValue>) ? 
                   default(TKey) : pair.Key;
}

Exception:

L'opérateur '==' ne peut pas s'appliquer aux opérandes De type 'System.Collections.Generic.KeyValuePair <chaîne, objet>' Et 'Système. Collections.Generic.KeyValuePair <string, object> '

Est-ce parce que, en tant que structure, KeyValuePair n'est pas nullable? Si tel est le cas, pourquoi, comme, vraisemblablement, default a été implémenté pour gérer les types non nullables?

MODIFIER 

Pour mémoire, j’ai choisi la réponse choisie par Chris Hannon, car il m’a donné ce que je cherchais, l’option la plus élégante et une explication succincte. Toutefois, j’encourage la lecture de @Dasuraga pour une explication très complète de la raison pour laquelle c’est l'affaire

33
johnc

Cela est dû au fait que KeyValuePair<TKey, TValue> ne définit pas d'opérateur == personnalisé et n'est pas inclus dans la liste prédéfinie des types de valeur pouvant l'utiliser.

Voici un lien vers la documentation MSDN pour cet opérateur.

Pour les types de valeur prédéfinis, l'opérateur d'égalité (==) renvoie true si les valeurs de ses opérandes sont égales, false sinon.

Dans ce cas, votre meilleur choix pour un contrôle d'égalité, car il ne s'agit pas d'une structure sur laquelle vous avez le contrôle, est d'appeler plutôt default(KeyValuePair<TKey,TValue>).Equals(pair).

53
Chris Hannon

(Si vous ne vous souciez pas de la discussion sur les génériques liée à cette erreur, vous pouvez aller directement à la fin pour votre "vraie" réponse)

Comme le dit l'erreur, il n'y a pas de test d'égalité pour KeyValuePairs (c'est-à-dire qu'il n'existe pas de méthode de comparaison intégrée). La raison en est d'éviter de devoir imposer des contraintes sur les types de KeyValuePairs (dans de nombreux cas, les comparaisons clé/valeur ne seraient jamais effectuées).

Évidemment, si vous voulez comparer ces KeyValuePairs, j'imagine que ce que vous voulez, c'est vérifier si les clés et les valeurs sont égales. Mais cela implique tout un gâchis, notamment le fait que TKey et TValue sont des types comparables (c'est-à-dire qu'elles implémentent l'interface IComparable)

Vous pouvez écrire votre propre fonction de comparaison entre keyvaluepairs, par exemple:

static bool KeyValueEqual<TKey , TValue>(KeyValuePair<TKey, TValue> fst, 
                                          KeyValuePair<TKey, TValue> snd) 
                                         where  TValue:IComparable
                                         where  TKey:IComparable
        {
            return (fst.Value.CompareTo(snd.Value)==0)
                     && (snd.Key.CompareTo(fst.Key)==0);
        }

(Excusez l'indentation terrible)

Ici, nous imposons que TKey et TValue soient comparables (via la fonction membre CompareTo).

La fonction CompareTo (telle que définie pour les types prédéfinis) renvoie 0 lorsque deux objets sont égaux, à la strcmp. a.ComparesTo (b) == 0 signifie que a et b sont "identiques" (en valeur, pas le même objet).

donc, cette fonction prendrait deux KVP (k, v) et (k ', v') et retournerait vrai si et seulement si k == k 'et v == v' (au sens intuitif).


Mais est-ce nécessaire? Il semble que votre test qui pose des problèmes repose sur une sorte de vérification du retour de FirstOrDefault.

Mais il y a une raison pour que votre fonction s'appelle FirstOrDefault :

Retourne le premier élément de la séquence Qui vérifie une condition ou A default valeur si aucun de ces éléments n'est trouvé .

(c'est moi qui souligne)

Cette fonction retourne les valeurs par défaut si quelque chose n'est pas trouvé , ce qui signifie que si votre prédicat n'est pas vérifié, vous obtiendrez un KeyValuePair égal à (default (TKey), default (TValue). 

Votre code vérifie donc (envisage de) vérifier si pair.Key == default (TKey), uniquement à renvoyer default (TKey) quand même . Ne serait-il pas plus logique de renvoyer pair.Key dès le début?

5
rtpg

Pour pouvoir utiliser l'opérateur d'égalité "==" sur toute classe ou structure, il faut écraser l'opérateur: http://msdn.Microsoft.com/en-us/library/ms173147(v=vs.80 ) .aspx

KeyValuePair ne le fait pas et vous obtenez donc l'erreur de compilation. Remarque, vous obtiendrez la même erreur si vous essayez juste ceci:

var k1 = new KeyValuePair<int,string>();
var k2 = new KeyValuePair<int,string>();

bool b = k1 == k2; //compile error

EDIT: Comme Eric Lippert m'a corrigé dans les commentaires, les classes n'ont évidemment pas besoin de redéfinir l'opérateur d'égalité pour que "==" soit valide. Cela compilera bien et fera un contrôle d’égalité de référence. Mon erreur.

4
BFree

Il échoue pour les mêmes raisons que les suivantes:

var kvp = new KeyValuePair<string,string>("a","b");
var res = kvp == kvp;

L'indice est naturellement dans le message d'erreur. (Cela n'a rien à voir avec default).

Operator '==' cannot be applied to operands of type 'System.Collections.Generic.KeyValuePair<string,string>' and 'System.Collections.Generic.KeyValuePair<string,string>'

L'opérateur == n'est pas défini pour KeyValuePair<T,U>.

Messages d'erreur FTW.

Bonne codage.

3
user166390

Les valeurs par défaut sont définies pour les types scalaires.

Posez-vous cette question: Qu'est-ce que cela signifie signifie pour que KVP ait une valeur par défaut? 

Pour les non-scalaires, la valeur par défaut est ce que vous obtenez en appelant le constructeur nil. En supposant que KVP Equals effectue une comparaison d'identité d'instance, je m'attendrais à ce qu'il renvoie false, car vous obtenez un nouvel objet chaque fois que le constructeur est appelé.

1
Peter Wone

Cela va dans une direction légèrement différente, mais je suppose que vous avez interrogé un dictionnaire pour obtenir ce résultat, puis vous voulez vérifier s'il a renvoyé un résultat valide ou non. 

J'ai trouvé que la meilleure méthode pour cela consistait à interroger la valeur réelle à la place de l'ensemble de KeyValuePair, comme ceci: 

var valitem = MyDict.Values.FirstOrDefault (x => x.Quelque chose == aVar); 

Maintenant, vous pouvez vérifier si valitem est null ou non. Encore une fois, cela ne répond pas directement à votre question, mais propose une approche alternative à votre objectif. 

0
Brady Moritz