IGrouping.ToList() - 处理空值

IGrouping.ToList() - Handling null values

我有一个简单的单行 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.


是否可以使用此空值来 return 我的 "one line" 查询中的空列表?

最明显的方法是删除 ToList() 调用并简单地 return 一个 IGrouping 结果,并且只在它不为空时调用 ToList(),但我很好奇它是否可以在线完成。

您需要使用 null-conditional operator after calling FirstOrDefault(). If this method returns null, the result of the entire expression will be null. You can then use the null-coalescing operatornull 结果转换为空列表。

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

问题出在.FirstOrDefault().ToList()。由于 FirstOrDefault() 可能 return 一个值 或 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();