tutorial sintaxis espaƱol ejemplos .net linq

.net - sintaxis - FirstOrDefault: valor predeterminado distinto de nulo



linq to sql (10)

Tal como lo entiendo, en Linq, el método FirstOrDefault () puede devolver un valor predeterminado de algo distinto de null.

No. O más bien, siempre devuelve el valor predeterminado para el tipo de elemento ... que es una referencia nula, el valor nulo de un tipo de valor que admite nulos o el valor natural de "todos los ceros" para un tipo de valor que no admite nulos.

¿Hay alguna manera particular de configurar esto para que si no hay ningún valor para una consulta en particular, se devuelva algún valor predefinido como valor predeterminado?

Para tipos de referencia, solo puede usar:

var result = query.FirstOrDefault() ?? otherDefaultValue;

Por supuesto, esto también le dará el "otro valor predeterminado" si el primer valor está presente, pero es una referencia nula ...

Tal como lo entiendo, en Linq, el método FirstOrDefault() puede devolver un valor Default de algo distinto de null. Lo que no he averiguado es qué tipo de cosas, aparte de null, pueden devolverse con este (y similar) método cuando no hay elementos en el resultado de la consulta. ¿Hay alguna manera particular de configurar esto para que si no hay ningún valor para una consulta en particular, se devuelva algún valor predefinido como valor predeterminado?


Acabo de tener una situación similar y estaba buscando una solución que me permita devolver un valor predeterminado alternativo sin ocuparme de él por el lado de la persona que llama cada vez que lo necesito. Lo que solemos hacer en caso de que Linq no sea compatible con lo que queremos, es escribir una nueva extensión que lo solucione. Eso fue lo que hice. Esto es lo que se me ocurrió (sin embargo, no probado):

public static class EnumerableExtensions { public static T FirstOrDefault<T>(this IEnumerable<T> items, T defaultValue) { foreach (var item in items) { return item; } return defaultValue; } public static T FirstOrDefault<T>(this IEnumerable<T> items, Func<T, bool> predicate, T defaultValue) { return items.Where(predicate).FirstOrDefault(defaultValue); } public static T LastOrDefault<T>(this IEnumerable<T> items, T defaultValue) { return items.Reverse().FirstOrDefault(defaultValue); } public static T LastOrDefault<T>(this IEnumerable<T> items, Func<T, bool> predicate, T defaultValue) { return items.Where(predicate).LastOrDefault(defaultValue); } }


Caso general, no solo para tipos de valor:

static class ExtensionsThatWillAppearOnEverything { public static T IfDefaultGiveMe<T>(this T value, T alternate) { if (value.Equals(default(T))) return alternate; return value; } } var result = query.FirstOrDefault().IfDefaultGiveMe(otherDefaultValue);

Nuevamente, esto realmente no puede decir si hubo algo en su secuencia, o si el primer valor fue el predeterminado.

Si te importa esto, podrías hacer algo como

static class ExtensionsThatWillAppearOnIEnumerables { public static T FirstOr<T>(this IEnumerable<T> source, T alternate) { foreach(T t in source) return t; return alternate; } }

y usar como

var result = query.FirstOr(otherDefaultValue);

aunque, como señala el Sr. Steak, esto podría hacerse igual de bien con .DefaultIfEmpty(...).First() .


Copiado de un comentario de @sloth

En lugar de YourCollection.FirstOrDefault() , podría usar YourCollection.DefaultIfEmpty(YourDefault).First() por ejemplo.

Ejemplo:

var viewModel = new CustomerDetailsViewModel { MainResidenceAddressSection = (MainResidenceAddressSection)addresses.DefaultIfEmpty(new MainResidenceAddressSection()).FirstOrDefault( o => o is MainResidenceAddressSection), RiskAddressSection = addresses.DefaultIfEmpty(new RiskAddressSection()).FirstOrDefault(o => !(o is MainResidenceAddressSection)), };


De la documentación para FirstOrDefault

[Devuelve] predeterminado (TSource) si el origen está vacío;

De la documentación por default(T) :

la palabra clave predeterminada, que devolverá nulo para los tipos de referencia y cero para los tipos de valores numéricos. Para las estructuras, devolverá a cada miembro de la estructura inicializada a cero o nulo dependiendo de si son valores o tipos de referencia. Para tipos de valores que aceptan valores de nulos, el valor predeterminado devuelve un System.Nullable, que se inicializa como cualquier estructura.

Por lo tanto, el valor predeterminado puede ser nulo o 0 dependiendo de si el tipo es una referencia o un tipo de valor, pero no puede controlar el comportamiento predeterminado.


En lugar de YourCollection.FirstOrDefault() , podría usar YourCollection.DefaultIfEmpty(YourDefault).First() por ejemplo.


En realidad, utilizo dos enfoques para evitar NullReferenceException cuando estoy trabajando con colecciones:

public class Foo { public string Bar{get; set;} } void Main() { var list = new List<Foo>(); //before C# 6.0 string barCSharp5 = list.DefaultIfEmpty(new Foo()).FirstOrDefault().Bar; //C# 6.0 or later var barCSharp6 = list.FirstOrDefault()?.Bar; }

Para C # 6.0 o posterior:

Usar ?. o ?[ para probar si es nulo antes de realizar un acceso de miembros a la documentación de operadores condicionales nulos

Ejemplo: var barCSharp6 = list.FirstOrDefault()?.Bar;

C # versión anterior:

Use DefaultIfEmpty() para recuperar un valor predeterminado si la secuencia está vacía. Documentación de MSDN

Ejemplo: string barCSharp5 = list.DefaultIfEmpty(new Foo()).FirstOrDefault().Bar;


Puedes usar DefaultIfEmpty seguido de First :

T customDefault = ...; IEnumerable<T> mySequence = ...; mySequence.DefaultIfEmpty(customDefault).First();


También puedes hacer esto

Band[] objects = { new Band { Name = "Iron Maiden" } }; first = objects.Where(o => o.Name == "Slayer") .DefaultIfEmpty(new Band { Name = "Black Sabbath" }) .FirstOrDefault(); // returns "Black Sabbath"

¡Esto solo usa linq - yipee!


Use DefaultIfEmpty() lugar de FirstOrDefault() .