Насколько я понимаю, в Linq метод FirstOrDefault()
может возвращать Default
значение, отличное от null. Что я не понял, так это то, какие вещи, кроме null, могут быть возвращены этим (и подобным) методом, когда в результате запроса нет элементов. Есть ли какой-то особый способ настроить это так, чтобы при отсутствии значения для определенного запроса какое-то предопределенное значение возвращалось как значение по умолчанию?
146
YourCollection.FirstOrDefault()
,YourCollection.DefaultIfEmpty(YourDefault).First()
например , вы можете использовать .MyCollection.Last().GetValueOrDefault(0)
для этого. В противном случае ответ @Jon Skeet ниже является правильным ИМО.Ответы:
Общий случай, не только для типов значений:
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);
Опять же , это не может точно сказать , если было что - нибудь в вашей последовательности, или , если первое значение было по умолчанию.
Если вам это небезразлично, вы можете сделать что-нибудь вроде
static class ExtensionsThatWillAppearOnIEnumerables { public static T FirstOr<T>(this IEnumerable<T> source, T alternate) { foreach(T t in source) return t; return alternate; } }
и использовать как
var result = query.FirstOr(otherDefaultValue);
хотя, как указывает г-н Стейк, с тем же успехом это может сделать
.DefaultIfEmpty(...).First()
.источник
<T>
в своих именах, но более серьезно то, чтоvalue == default(T)
это не работает (потому что, кто знает,T
можно ли сравнивать их для равенства?)EqualityComparer<T>.Default.Equals(value, default(T))
чтобы избежать бокса и избежать исключения, если значение равноnull
Нет. Или, скорее, он всегда возвращает значение по умолчанию для типа элемента ... которое является либо нулевой ссылкой, нулевым значением типа значения, допускающим значение NULL, либо естественным значением «все нули» для типа значения, не допускающего значения NULL.
Для ссылочных типов вы можете просто использовать:
var result = query.FirstOrDefault() ?? otherDefaultValue;
Конечно, это также даст вам «другое значение по умолчанию», если присутствует первое значение, но это пустая ссылка ...
источник
int
. Я предпочитаю использоватьDefaultIfEmpty
:src.Where(filter).DefaultIfEmpty(defaultValue).First()
. Работает как для типа значения, так и для ссылочного типа.Вы можете использовать DefaultIfEmpty, за которым следует First :
T customDefault = ...; IEnumerable<T> mySequence = ...; mySequence.DefaultIfEmpty(customDefault).First();
источник
DefaultIfEmpty
- она работает со всеми API , которые должны по умолчанию должны быть указаны:First()
,Last()
и т.д. Как пользователь, вам не нужно запоминать , какие интерфейсы позволяют указать значение по умолчанию , которые не делают. Очень элегантный!Из документации для FirstOrDefault
Из документации по умолчанию (T) :
Поэтому значение по умолчанию может быть нулевым или нулевым в зависимости от того, является ли тип ссылочным или типом значения, но вы не можете управлять поведением по умолчанию.
источник
Скопировано из комментария @sloth
Вместо
YourCollection.FirstOrDefault()
,YourCollection.DefaultIfEmpty(YourDefault).First()
например , вы можете использовать .Пример:
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)), };
источник
DefaultIfEmpty
возвращает значение по умолчанию, ЕСЛИ коллекция пуста (имеет 0 элементов). Если вы используетеFirst
WITH совпадающее выражение, как в вашем примере, и это условие не находит ни одного элемента, возвращаемое значение будет пустым.Вы также можете сделать это
Band[] objects = { new Band { Name = "Iron Maiden" } }; first = objects.Where(o => o.Name == "Slayer") .DefaultIfEmpty(new Band { Name = "Black Sabbath" }) .FirstOrDefault(); // returns "Black Sabbath"
Здесь используется только linq - yipee!
источник
FirstOrDefault
вместоFirst
. Согласно msdn.microsoft.com/en-us/library/bb340482.aspx , рекомендуемое использованиеFirst
На самом деле,
NullReferenceException
когда я работаю с коллекциями , я стараюсь избегать двух подходов :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; }
Для C # 6.0 или новее:
Используйте
?.
или,?[
чтобы проверить, является ли значение null перед выполнением доступа к члену документация по операторам с условием NULLПример:
var barCSharp6 = list.FirstOrDefault()?.Bar;
C # более старая версия:
Используйте
DefaultIfEmpty()
для получения значения по умолчанию, если последовательность пуста. Документация MSDNПример:
string barCSharp5 = list.DefaultIfEmpty(new Foo()).FirstOrDefault().Bar;
источник
Вместо
YourCollection.FirstOrDefault()
,YourCollection.DefaultIfEmpty(YourDefault).First()
например , вы можете использовать .источник
У меня была аналогичная ситуация, и я искал решение, которое позволяет мне возвращать альтернативное значение по умолчанию, не заботясь об этом на стороне вызывающего абонента каждый раз, когда мне это нужно. Что мы обычно делаем, если Linq не поддерживает то, что мы хотим, - это писать новое расширение, которое позаботится об этом. Вот что я сделал. Вот что я придумал (хотя и не проверял):
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); } }
источник
Я знаю, что это было давно, но я добавлю к этому, основываясь на самом популярном ответе, но с небольшим расширением, я хотел бы поделиться следующим:
static class ExtensionsThatWillAppearOnIEnumerables { public static T FirstOr<T>(this IEnumerable<T> source, Func<T, bool> predicate, Func<T> alternate) { var thing = source.FirstOrDefault(predicate); if (thing != null) return thing; return alternate(); } }
Это позволяет мне называть его встроенным в моем собственном примере, с которым у меня возникли проблемы:
_controlDataResolvers.FirstOr(x => x.AppliesTo(item.Key), () => newDefaultResolver()).GetDataAsync(conn, item.ToList())
Поэтому для меня я просто хотел, чтобы преобразователь по умолчанию использовался встроенным, я могу выполнить свою обычную проверку, а затем передать функцию, чтобы класс не создавался, даже если он не используется, это функция, которую нужно выполнять, когда это необходимо!
источник
Используйте
DefaultIfEmpty()
вместоFirstOrDefault()
.источник