IGrouping.ToList () - Обработка нулевых значений - PullRequest
1 голос
/ 14 марта 2019

У меня есть простой однострочный запрос linq, чтобы найти элементы в списке, которые содержат наибольшее значение свойства, и создать новый список с ними, только если существует определенное количество элементов, соответствующих этому критерию. Например:

List<MyObject> objects = new List<MyObject>()
{
    new MyObject() { MyValue = 6 },
    new MyObject() { MyValue = 7 },
    new MyObject() { MyValue = 7 },
    new MyObject() { MyValue = 8 },
    new MyObject() { MyValue = 8 },
};

int countRequired = 2;

List<MyObject> highestValue2Required = objects.GroupBy( o => o.MyValue )
    .OrderByDescending( g => g.Key )
    .Where( g => g.Count() == countRequired )
    .FirstOrDefault()
    .ToList();

Если в моем списке есть данные, удовлетворяющие этим условиям, будет создан новый список, содержащий два экземпляра MyObject, где MyValue = 8.

Однако, если нет данных, соответствующих критериям, я получаю исключение, потому что IGrouping результат запроса равен нулю, и вызов ToList() для этого не работает. Например:

int countRequired = 3;

List<MyObject> highestValue3Required = objects.GroupBy( o => o.MyValue )
    .OrderByDescending( g => g.Key )
    .Where( g => g.Count() == countRequired )
    .FirstOrDefault()
    .ToList();

Поскольку в списке есть только два экземпляра MyObject, где MyValue равно наибольшему значению (равному 8), запрос вызывает исключение

System.ArgumentNullException: 'Value cannot be null.


Возможно ли это нулевое значение для возврата пустого списка в моем запросе "одна строка"?

Очевидный способ сделать это состоит в том, чтобы удалить вызов ToList() и просто вернуть результат IGrouping, и вызывать ToList() только тогда, когда он не равен нулю, но мне любопытно, можно ли это сделать в соответствии.

Ответы [ 2 ]

1 голос
/ 14 марта 2019

Проблема в .FirstOrDefault().ToList(). Поскольку FirstOrDefault() может возвращать значение или NULL , вы не можете напрямую вызвать .ToList().

Если вы хотите, чтобы конечный результат был null, если совпадений нет, вы можете просто добавить нулевой кондиционер (?) к вызову FirstOrDefault():

List<MyObject> highestValue2Required = objects.GroupBy(o => o.MyValue)
    .OrderByDescending(g => g.Key)
    .Where(g => g.Count() == countRequired)
    .FirstOrDefault()?
    .ToList();

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

static class Extensions
{
    public static IEnumerable<T> OrEmptyIfNull<T>(this IEnumerable<T> source)
    {
        return (source ?? Enumerable.Empty<T>());
    }
}

Теперь вы можете легко сделать что-то вроде этого:

List<MyObject> highestValue2Required = objects.GroupBy(o => o.MyValue)
    .OrderByDescending(g => g.Key)
    .Where(g => g.Count() == countRequired)
    .FirstOrDefault()
    .OrEmptyIfNull()
    .ToList();
1 голос
/ 14 марта 2019

Вы захотите использовать нулевой условный оператор после вызова FirstOrDefault().Если этот метод возвращает null, результат всего выражения будет нулевым.Затем вы можете использовать оператор объединения нулей , чтобы преобразовать результат null в пустой список.

var highestValue3Required = objects.GroupBy( o => o.MyValue )
    .OrderByDescending( g => g.Key )
    .Where( g => g.Count() == countRequired )
    .FirstOrDefault()
    ?.ToList() ?? new List<MyObject>();
...