Comment puis-je m'assurer que FirstOrDefault <KeyValuePair> a renvoyé une valeur

91

Voici une version simplifiée de ce que j'essaie de faire:

var days = new Dictionary<int, string>();
days.Add(1, "Monday");
days.Add(2, "Tuesday");
...
days.Add(7, "Sunday");

var sampleText = "My favorite day of the week is 'xyz'";
var day = days.FirstOrDefault(x => sampleText.Contains(x.Value));

Puisque 'xyz' n'est pas présent dans le dictionnaire, la méthode FirstOrDefault ne retournera pas de valeur valide. Je veux pouvoir vérifier cette situation mais je me rends compte que je ne peux pas comparer le résultat à "null" car KeyValuePair est une struc. Le code suivant n'est pas valide:

if (day == null) {
    System.Diagnotics.Debug.Write("Couldn't find day of week");
}

Nous vous essayez de compiler le code, Visual Studio lève l'erreur suivante:

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

Comment puis-je vérifier que FirstOrDefault a renvoyé une valeur valide?

desautelsj
la source
1
Vous avez un bogue là-bas, mais je suppose que c'est une chose de copier-coller: les jours ne sont pas une liste, et vous ne pouvez pas utiliser add on KeyValuePair.
Kobi
ooops ... vous avez raison, je tapais de mémoire et j'ai évidemment fait une erreur. Merci de l'avoir signalé.
desautelsj
1
C'était probablement: var days = new Dictionary <int, string> ();
Even Mien

Réponses:

156

FirstOrDefaultne retourne pas null, il retourne default(T).
Vous devriez vérifier:

var defaultDay = default(KeyValuePair<int, string>);
bool b = day.Equals(defaultDay);

Depuis MSDN -Enumerable.FirstOrDefault<TSource> :

par défaut ( TSource ) si la source est vide; sinon, le premier élément de source .

Remarques:

Kobi
la source
17
+1, KeyValuePair est un type valeur (struct), pas un type référence (classe) ou un type valeur Nullable, il ne peut donc pas être null.
Lucas
6
@ paper1337 - Merci, mais où me manque- typeoft- il ? Ce code se compile et fonctionne.
Kobi
3
Je suis venu ici parce que ce n'était pas clair pour moi, ce default(KeyValuePair<T1, T2>)qui en résulterait. Ok, cela aurait dû être assez évident, que cela aboutirait à un KVP vide. Mais comme "être évident" n'est pas une bonne approche pour écrire des applications appropriées (et mon implémentation actuelle est trop complexe pour provoquer clairement / proprement ce cas), je l'ai essayé avec un nouveau projet et - en effet - il a renvoyé un KeyValuePairavec des propriétés Keyet Valueétant les deux NULL.... juste pour protéger d'autres personnes ces 5 minutes de bêtise ;-)
Nicolas
@Nicolas - Pas de stupidité ici. C'est toujours une bonne idée de vérifier par vous-même et de vous assurer que vous comprenez votre code. J'ai ajouté un lien vers le defaultmot - clé, il manque clairement ici. Merci!
Kobi le
1
@JeffBridgman - C'est vraiment un bon point! Plus précisément ici, ce n'est pas possible, car nous travaillons avec KeyValuePair. Si vous aviez un code générique, il day.Equalsn'est même pas sûr de null, et j'aurais utiliséEqualityComparer<T>.Default.Equals(day, defaultDay)
Kobi
54

C'est le moyen le plus clair et le plus concis à mon avis:

var matchedDays = days.Where(x => sampleText.Contains(x.Value));
if (!matchedDays.Any())
{
    // Nothing matched
}
else
{
    // Get the first match
    var day = matchedDays.First();
}

Cela évite complètement d'utiliser des trucs étranges de valeur par défaut pour les structures.

paix à l'extérieur
la source
13
Le problème avec cela, c'est qu'il est possible (selon l'implémentation) que les jours énumérables soient énumérés deux fois, ou pire encore, retournent des valeurs différentes entre les appels Any () et First ()
Ray Booysen
@RayBooysen Un appel de ToArray ou ToList résout le problème et vous pouvez utiliser Count / Length et un indexeur.
Console
1
Notez que la réponse de @ Ray ne s'applique pas ici, car daysc'est un fichier Dictionary<int,string>. Il sera donc considéré comme un IEnumerable<KeyValuePair<int,string>>, se comportant alors comme prévu quand Any()et First()sera appelé. Je suppose qu'il existe d'autres implémentations qui peuvent se comporter différemment comme IEnumerable<>. Je ne sais pas si je manque quelque chose.
Emanuele Bellini
0

Vous pouvez le faire à la place:

var days = new Dictionary<int?, string>();   // replace int by int?
days.Add(1, "Monday");
days.Add(2, "Tuesday");
...
days.Add(7, "Sunday");

var sampleText = "My favorite day of the week is 'xyz'";
var day = days.FirstOrDefault(x => sampleText.Contains(x.Value));

puis :

if (day.Key == null) {
    System.Diagnotics.Debug.Write("Couldn't find day of week");
}
Jocelyn Marcotte
la source