Wie kann ich sicherstellen, dass FirstOrDefault <KeyValuePair> einen Wert zurückgegeben hat?

91

Hier ist eine vereinfachte Version von dem, was ich versuche zu tun:

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));

Da 'xyz' im Wörterbuch nicht vorhanden ist, gibt die FirstOrDefault-Methode keinen gültigen Wert zurück. Ich möchte in der Lage sein, nach dieser Situation zu suchen, aber mir ist klar, dass ich das Ergebnis nicht mit "null" vergleichen kann, da KeyValuePair eine Struktur ist. Der folgende Code ist ungültig:

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

Wenn Sie versuchen, den Code zu kompilieren, gibt Visual Studio den folgenden Fehler aus:

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

Wie kann ich überprüfen, ob FirstOrDefault einen gültigen Wert zurückgegeben hat?

desautelsj
quelle
1
Sie haben dort einen Fehler, aber ich nehme an, es ist eine Sache zum Kopieren und Einfügen: Tage sind keine Liste, und Sie können Add-on-KeyValuePair nicht verwenden.
Kobi
ooops ... Sie haben Recht, ich habe aus dem Gedächtnis getippt und offensichtlich einen Fehler gemacht. Vielen Dank für den Hinweis.
Desautelsj
1
Es war wahrscheinlich: var days = new Dictionary <int, string> ();
Sogar Mien

Antworten:

155

FirstOrDefaultgibt nicht null zurück, sondern zurück default(T).
Sie sollten überprüfen nach:

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

Von MSDN -Enumerable.FirstOrDefault<TSource> :

Standard ( TSource ), wenn die Quelle leer ist; Andernfalls das erste Element in der Quelle .

Anmerkungen:

Kobi
quelle
16
+1, KeyValuePair ist ein Werttyp (struct), kein Referenztyp (Klasse) oder ein nullbarer Werttyp, daher kann er nicht null sein.
Lucas
6
@ paper1337 - Danke, aber wo vermisse ich typeof? Dieser Code wird kompiliert und funktioniert.
Kobi
3
Ich bin hierher gekommen, weil mir nicht klar war, was daraus default(KeyValuePair<T1, T2>)resultieren würde. Ok, es hätte ziemlich offensichtlich sein müssen, dass es zu einem leeren KVP führen würde. Da "offensichtlich sein" kein guter Ansatz ist, um richtige Anwendungen zu schreiben (und meine aktuelle Implementierung zu komplex ist, um diesen Fall klar / sauber zu provozieren), habe ich es mit einem neuen Projekt versucht und - in der Tat - ein KeyValuePairmit Eigenschaften Keyund ValueSein zurückgegeben beides NULL.... nur um ein paar anderen Leuten diese 5 Minuten Dummheit zu ersparen ;-)
Nicolas
@Nicolas - Keine Dummheit hier. Es ist immer eine gute Idee, sich selbst zu überprüfen und sicherzustellen, dass Sie Ihren Code verstehen. Ich habe einen Link zum defaultKeyword hinzugefügt, der hier eindeutig fehlt. Vielen Dank!
Kobi
1
@ JeffBridgman - Das ist wirklich ein guter Punkt! Insbesondere hier ist es nicht möglich, weil wir mit arbeiten KeyValuePair. Wenn Sie generischen Code hätten, day.Equalsist nicht einmal null-sicher, und ich hätte verwendetEqualityComparer<T>.Default.Equals(day, defaultDay)
Kobi
53

Dies ist meiner Meinung nach der klarste und prägnanteste Weg:

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

Dies wird vollständig umgangen, indem seltsame Standardwerte für Strukturen verwendet werden.

Frieden draußen
quelle
13
Das Problem dabei ist, dass (abhängig von der Implementierung) die Möglichkeit besteht, dass die aufzählbaren Tage zweimal aufgezählt werden oder, noch schlimmer, unterschiedliche Werte zwischen den Aufrufen Any () und First () zurückgeben
Ray Booysen
@ RayBooysen Ein Aufruf von ToArray oder ToList löst das Problem und Sie können Count / Length und einen Indexer verwenden.
Konsole
1
Beachten Sie, dass die Antwort von @ Ray hier nicht gilt, da daysa Dictionary<int,string>. Es wird also als betrachtet IEnumerable<KeyValuePair<int,string>>und verhält sich dann wie erwartet, wenn Any()und First()aufgerufen werden. Ich denke, dass es andere Implementierungen gibt, die sich anders verhalten können als IEnumerable<>. Ich weiß nicht, ob mir etwas fehlt.
Emanuele Bellini
0

Sie können dies stattdessen tun:

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));

und dann :

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