¿Cómo puedo asegurarme de que FirstOrDefault <KeyValuePair> haya devuelto un valor?

91

Aquí hay una versión simplificada de lo que estoy tratando de hacer:

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

Dado que 'xyz' no está presente en el diccionario, el método FirstOrDefault no devolverá un valor válido. Quiero poder verificar esta situación, pero me doy cuenta de que no puedo comparar el resultado con "nulo" porque KeyValuePair es una estructura. El siguiente código no es válido:

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

Si intenta compilar el código, Visual Studio arroja el siguiente error:

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

¿Cómo puedo comprobar que FirstOrDefault ha devuelto un valor válido?

desautelsj
fuente
1
Tiene un error allí, pero supongo que es una cosa de copiar y pegar: los días no son una lista y no puede usar agregar en KeyValuePair.
Kobi
ooops ... tienes razón. Estaba escribiendo de memoria y obviamente cometí un error. Gracias por mencionarlo.
desautelsj
1
Probablemente fue: var days = new Dictionary <int, string> ();
Even Mien

Respuestas:

156

FirstOrDefaultno devuelve nulo, devuelve default(T).
Debes buscar:

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

Desde MSDN -Enumerable.FirstOrDefault<TSource> :

predeterminado ( TSource ) si la fuente está vacía; de lo contrario, el primer elemento de la fuente .

Notas:

Kobi
fuente
17
+1, KeyValuePair es un tipo de valor (estructura), no un tipo de referencia (clase) o un tipo de valor anulable, por lo que no puede ser nulo.
Lucas
6
@ paper1337 - Gracias, pero ¿dónde me pierdo typeof? Este código se compila y funciona.
Kobi
3
Vine aquí porque no estaba claro para mí, lo default(KeyValuePair<T1, T2>)que resultaría. Ok, debería haber sido bastante obvio, que resultaría en un KVP vacío. Pero como "ser obvio" no es un buen enfoque para escribir aplicaciones adecuadas (y mi implementación actual es demasiado compleja para provocar este caso de manera clara / limpia), lo probé con un nuevo proyecto y, de hecho, devolvió un KeyValuePaircon propiedades Keyy Valuesiendo ambos NULL.... solo para salvar a otras personas estos 5 minutos de estupidez ;-)
Nicolas
@Nicolas - No hay estupidez aquí. Siempre es una buena idea comprobarlo usted mismo y asegurarse de que comprende su código. Agregué un enlace a la defaultpalabra clave, claramente falta aquí. ¡Gracias!
Kobi
1
@JeffBridgman - ¡Ese es realmente un buen punto! Específicamente aquí no es posible, porque estamos trabajando con KeyValuePair. Si tuviera un código genérico, day.Equalsni siquiera es seguro para nulos, y lo habría usadoEqualityComparer<T>.Default.Equals(day, defaultDay)
Kobi
54

Esta es la forma más clara y concisa en mi opinión:

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

Esto evita por completo el uso de extraños valores predeterminados para estructuras.

paz fuera
fuente
13
El problema con esto es que existe la posibilidad (dependiendo de la implementación) de que los días enumerables se enumeren dos veces, o peor aún, devuelvan valores diferentes entre las llamadas Any () y First ()
Ray Booysen
@RayBooysen Una llamada de ToArray o ToList resuelve el problema y puede usar Count / Length y un Indexer.
Consola
1
Tenga en cuenta que la respuesta de @ Ray no se aplica aquí, porque dayses un Dictionary<int,string>. Por lo tanto, será considerado como una IEnumerable<KeyValuePair<int,string>>, entonces se comporta como se espera cuando Any()y First()son llamados. Supongo que hay otras implementaciones que pueden comportarse de manera diferente como IEnumerable<>. No sé si me estoy perdiendo algo.
Emanuele Bellini
0

Puedes hacer esto en su lugar:

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

y entonces :

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