Como posso ter certeza de que FirstOrDefault <KeyValuePair> retornou um valor

91

Esta é uma versão simplificada do que estou tentando fazer:

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

Como 'xyz' não está presente no dicionário, o método FirstOrDefault não retornará um valor válido. Quero ser capaz de verificar essa situação, mas percebo que não posso comparar o resultado com "null" porque KeyValuePair é uma estrutura. O seguinte código é inválido:

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

Quando você tenta compilar o código, o Visual Studio gera o seguinte erro:

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

Como posso verificar se FirstOrDefault retornou um valor válido?

desautelsj
fonte
1
Você tem um bug aí, mas presumo que seja uma coisa de copiar e colar: dias não é uma lista e você não pode usar o add em KeyValuePair.
Kobi
opa ... você está correto, eu estava digitando de memória e obviamente cometi um erro. Obrigado por apontar isso.
desautelsj
1
Provavelmente foi: var days = new Dictionary <int, string> ();
Even Mien

Respostas:

155

FirstOrDefaultnão retorna nulo, ele retorna default(T).
Você deve verificar:

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

Do MSDN -Enumerable.FirstOrDefault<TSource> :

padrão ( TSource ) se a fonte estiver vazia; caso contrário, o primeiro elemento na origem .

Notas:

Kobi
fonte
16
+1, KeyValuePair é um tipo de valor (struct), não um tipo de referência (classe) ou um tipo de valor anulável, portanto, não pode ser nulo.
Lucas
6
@ paper1337 - Obrigado, mas onde estou perdendo typeof? Este código compila e funciona.
Kobi,
3
Vim aqui porque não estava claro para mim o default(KeyValuePair<T1, T2>)que resultaria. Ok, deveria ter sido bastante óbvio, que resultaria em um KVP vazio. Mas como "ser óbvio" não é uma boa abordagem para escrever aplicativos adequados (e minha implementação atual é muito complexa para provocar de forma clara / limpa este caso), eu tentei com um novo projeto e - de fato - ele retornou um KeyValuePaircom propriedades Keye Valuesendo ambos NULL.... apenas para proteger outras pessoas nestes 5 minutos de estupidez ;-)
Nicolas
@Nicolas - Sem estupidez aqui. É sempre uma boa ideia verificar por si mesmo e certificar-se de que entendeu seu código. Eu adicionei um link para a defaultpalavra-chave, ele está claramente faltando aqui. Obrigado!
Kobi
1
@JeffBridgman - Esse é realmente um bom ponto! Especificamente aqui não é possível, porque estamos trabalhando com KeyValuePair. Se você tivesse um código genérico, day.Equalsnem mesmo é seguro para nulos, e eu teria usadoEqualityComparer<T>.Default.Equals(day, defaultDay)
Kobi
53

Esta é a forma mais clara e concisa em minha opinião:

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

Isso contorna completamente o uso de coisas estranhas de valor padrão para estruturas.

lado da paz
fonte
13
O problema com isso é que existe a possibilidade (dependendo da implementação) de que os dias enumeráveis ​​sejam enumerados duas vezes ou, pior ainda, retornem valores diferentes entre as chamadas Any () e First ()
Ray Booysen
@RayBooysen Uma chamada de ToArray ou ToList resolve o problema e você pode usar Count / Length e um Indexer.
Console de
1
Observe que a resposta de @Ray não se aplica aqui, porque daysé a Dictionary<int,string>. Portanto, ele será considerado como um IEnumerable<KeyValuePair<int,string>>, comportando-se conforme o esperado quando Any()e First()for chamado. Eu acho que existem outras implementações que podem se comportar de forma diferente como IEnumerable<>. Não sei se estou perdendo alguma coisa.
Emanuele Bellini,
0

Você pode fazer isso:

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

e depois :

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