FirstOrDefault: значение по умолчанию, отличное от нуля

Как я понимаю, в Linq метод FirstOrDefault() может возвращать значение Default чего-то другого, кроме null. То, что я не разработал, - это то, что этот метод (и аналогичный) может быть возвращен другим, кроме null, когда в результате запроса нет элементов. Есть ли какой-либо особый способ, которым это можно настроить так, чтобы, если для конкретного запроса нет значения, в качестве значения по умолчанию возвращается значение определенного значения?

Ответ 1

Общий случай, а не только для типов значений:

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().

Ответ 2

Как я понимаю, в Linq метод FirstOrDefault() может возвращать значение по умолчанию, отличное от нуля.

Нет. Вернее, он всегда возвращает значение по умолчанию для типа элемента... которое является либо нулевой ссылкой, либо нулевым значением типа значения NULL, либо натуральным значением "все нули" для типа значения, не имеющего значения NULL.

Есть ли какой-либо конкретный способ, которым это можно настроить, чтобы, если для конкретного запроса нет значения, в качестве значения по умолчанию возвращается значение определенного значения?

Для ссылочных типов вы можете просто использовать:

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

Конечно, это также даст вам "другое значение по умолчанию", если первое значение присутствует, но является нулевой ссылкой...

Ответ 3

Вы можете использовать DefaultIfEmpty, а затем Сначала:

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

Ответ 4

Из документации для FirstOrDefault

[Возвращает] значение по умолчанию (TSource), если источник пуст;

Из документации для по умолчанию (T):

ключевое слово по умолчанию, которое будет возвращать значение null для ссылочных типов и ноль для числовых типов значений. Для structs он возвращает каждый член структуры, инициализированный до нуля или null, в зависимости от того, являются ли они значениями или ссылочными типами. Для типов значений с нулевым значением по умолчанию возвращает System.Nullable, который инициализируется как любая структура.

Следовательно, значение по умолчанию может быть нулевым или 0 в зависимости от того, является ли тип ссылкой или типом значения, но вы не можете управлять поведением по умолчанию.

Ответ 5

Скопировано из комментария @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)),
    };

Ответ 6

Вы также можете сделать это

    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!

Ответ 7

На самом деле, я использую два подхода, чтобы избежать 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;
}

Для С# 6.0 или новее:

Использовать ?. или ?[, чтобы проверить, является ли значение null, прежде чем выполнить доступ к членству Документация Null-условных операторов

Пример: var barCSharp6 = list.FirstOrDefault()?.Bar;

Старая версия С#:

Используйте DefaultIfEmpty() для получения значения по умолчанию, если последовательность пуста. Документация MSDN

Пример: string barCSharp5 = list.DefaultIfEmpty(new Foo()).FirstOrDefault().Bar;

Ответ 8

У меня была аналогичная ситуация, и я искал решение, которое позволяет мне возвращать альтернативное значение по умолчанию, не заботясь о нем со стороны вызывающего абонента каждый раз, когда мне это нужно. Что мы обычно делаем в случае, если 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);
    }
}

Ответ 9

Вместо YourCollection.FirstOrDefault() вы можете использовать YourCollection.DefaultIfEmpty(YourDefault).First(), например.

Ответ 10

Используйте DefaultIfEmpty() вместо FirstOrDefault().