Jak mogę się upewnić, że FirstOrDefault <KeyValuePair> zwrócił wartość

91

Oto uproszczona wersja tego, co próbuję zrobić:

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

Ponieważ „xyz” nie jest obecny w słowniku, metoda FirstOrDefault nie zwróci prawidłowej wartości. Chcę mieć możliwość sprawdzenia tej sytuacji, ale zdaję sobie sprawę, że nie mogę porównać wyniku z wartością „null”, ponieważ KeyValuePair jest strukturą. Poniższy kod jest nieprawidłowy:

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

Podczas próby skompilowania kodu program Visual Studio zgłasza następujący błąd:

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

Jak mogę sprawdzić, czy FirstOrDefault zwrócił prawidłową wartość?

desautelsj
źródło
1
Masz tam błąd, ale zakładam, że jest to kwestia kopiowania i wklejania: dni nie są listą i nie możesz użyć dodatku na KeyValuePair.
Kobi
ooops ... masz rację Pisałem z pamięci i oczywiście popełniłem błąd. Dzięki za wskazanie tego.
desautelsj
1
Prawdopodobnie było to: var days = new Dictionary <int, string> ();
Nawet Mien

Odpowiedzi:

156

FirstOrDefaultnie zwraca null, zwraca default(T).
Powinieneś sprawdzić:

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

Z MSDN -Enumerable.FirstOrDefault<TSource> :

domyślnie ( TSource ), jeśli źródło jest puste; w przeciwnym razie pierwszy element w source .

Uwagi:

Kobi
źródło
17
+1, KeyValuePair jest typem wartości (struct), a nie typem referencyjnym (klasa) ani typem wartości dopuszczającej wartość null, więc nie może mieć wartości null.
Lucas
6
@ paper1337 - Dzięki, ale gdzie mi brakuje typeof? Ten kod kompiluje się i działa.
Kobi
3
Przyszedłem tutaj, ponieważ nie było dla mnie jasne, co default(KeyValuePair<T1, T2>)z tego wyniknie. Ok, powinno być dość oczywiste, że spowoduje to pusty KVP. Ale ponieważ "bycie oczywistym" nie jest dobrym podejściem do pisania poprawnych aplikacji (a moja obecna implementacja jest zbyt złożona, aby jasno / czysto sprowokować ten przypadek), wypróbowałem ją z nowym projektem i - rzeczywiście - zwróciła KeyValuePairz właściwościami Keyi Valuebyciem jedno i drugie NULL... tylko po to, żeby uratować innym ludziom te 5 minut głupoty ;-)
Nicolas.
@Nicolas - Żadnej głupoty tutaj. Zawsze dobrze jest sprawdzić samemu i upewnić się, że rozumiesz kod. Dodałem link do defaultsłowa kluczowego, którego tutaj wyraźnie brakuje. Dzięki!
Kobi,
1
@JeffBridgman - To naprawdę dobra uwaga! Konkretnie tutaj nie jest to możliwe, ponieważ pracujemy z KeyValuePair. Gdybyś miał ogólny kod, day.Equalsnie jest nawet bezpieczny dla null i EqualityComparer<T>.Default.Equals(day, defaultDay)
użyłbym
55

Moim zdaniem jest to najbardziej jasny i zwięzły sposób:

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

To całkowicie rozwiązuje problem przy użyciu dziwnych wartości domyślnych dla struktur.

pokój na zewnątrz
źródło
13
Problem z tym polega na tym, że istnieje możliwość (w zależności od implementacji), że wyliczalne dni będą wyliczane dwukrotnie lub, co gorsza, zwracają różne wartości między wywołaniami Any () i First ()
Ray Booysen
@RayBooysen Wywołanie ToArray lub ToList rozwiązuje problem i możesz użyć Count / Length i Indexer.
Konsola
1
Zauważ, że odpowiedź @ Ray nie ma tutaj zastosowania, ponieważ daysjest Dictionary<int,string>. Więc będzie traktowane jako IEnumerable<KeyValuePair<int,string>>, a następnie zachowuje się zgodnie z oczekiwaniami, gdy Any()i First()są wywoływane. Wydaje mi się, że istnieją inne implementacje, które mogą zachowywać się inaczej niż IEnumerable<>. Nie wiem, czy czegoś mi brakuje.
Emanuele Bellini
0

Zamiast tego możesz to zrobić:

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

i wtedy :

if (day.Key == null) {
    System.Diagnotics.Debug.Write("Couldn't find day of week");
}
Jocelyn Marcotte
źródło