如何在LINQ中将空列表视为空列表处理?

9
以下是一些LinqPad测试代码。当运行此代码时,由于第二个“item”实例具有null子项列表而不是空列表,因此会出现错误。
我希望以完全相同的方式处理两种情况(null或空列表),但我想知道是否有比仅在列表上进行null检查并在存在null时初始化空列表更清晰的方法。
换句话说,我可以这样做:
from si in (i.subitems == null ? new List<item>() : i.subitems)

但是这样有点丑,我想知道如何改进呢?
public class item
{
    public string itemname { get; set; }
    public List<item> subitems { get; set; }
}

void Main()
{
    List<item> myItemList = new List<item>() 
    {
        new item 
        {
            itemname = "item1",
            subitems = new List<item>()
            {
                new item { itemname = "subitem1" },
                new item { itemname = "subitem2" }
            }
        },
        new item 
        {
            itemname = "item2"
        }
    };

    myItemList.Dump();

    var res = (from i in myItemList
            from si in i.subitems
            select new {i.itemname, subitemname = si.itemname}).ToList();

    res.Dump();
}

作为一个额外的问题,这个LINQ查询能否表示为lambda,并以同样的方式处理null值?
谢谢,Chris
4个回答

15

您可以使用null 合并运算符

var res = (from i in myItemList
           from si in i.subitems ?? new List<item>()
           select new { i.itemname, subitemname = si.itemname }).ToList();

但我认为你应该将空的过滤掉

var res = (from i in myItemList
           where i.subitems != null
           from si in i.subitems
           select new { i.itemname, subitemname = si.itemname }).ToList();

关于 lambda 版本,你可以这样说:

var res = myItemList.Where(x => x.subitems != null)
                    .SelectMany(
                        x => x.subitems.Select(
                            y => new { x.itemname, subitemname = y.itemname }
                        )
                     );

但查询语法版本更易读。


实际上,第二个选项非常易读,而且并不意味着需要创建一个新列表,然后将其忽略。谢谢。 - Chris Simpson
@Chris Simpson:我添加了lambda版本,因为你要求这样做。查询语法版本更易读。 - jason
1
我真的认为where子句是最干净的解决方案,所以我将其标记为答案。我只是好奇lambda等效性,但我同意,它不太可读。谢谢。 - Chris Simpson

11
from si in (i.subitems ?? new List<item>())

那怎么样?


1
是的,那比我的好(我现在很后悔一开始没有这么做),但这仍然意味着创建一个对象仅仅是为了消除它,这感觉不好。 - Chris Simpson
@captncraig 如需了解 ?? 的其他用途,请参阅 https://dev59.com/EnI-5IYBdhLWcg3wwbZL#1689544 - Marcel Gosselin

10

你可以添加一个(邪恶的)扩展方法来为你完成工作。

public static IEnumerable<T> EnsureNotEmpty<T>(this IEnumerable<T> enumerable) {
  if ( enumerable == null ) {
    return Enumerable.Empty<T>();
  } else { 
    return enumerable;
  }
}

“Enumerable.Repeat<T>(0)”比“Enumerable.Empty<T>()”更好吗? - bdukes
@bdukes,Enumerable.Empty更好,因为它更能声明你的意图。但出于某些原因,我总是忘记它是框架的一部分,而使用Repeat(0)代替。 - JaredPar
4
这个方法应该被命名为 EnsureNotNull(...),因为 EnsureNotEmpty(...) 听起来像是会添加一个明确的项。 :-> - herzmeister
4
为什么你说这是邪恶的?我还挺喜欢这种做法的。 - Darragh

0

另一种方法是不允许子项为空。您可以使项目构造函数默认将子项设置为空列表,然后不允许在子项设置器中使用null。

当然,这假设您可以访问修改项目。

如Hunter Daley所指出的那样,空合并运算符就是您要找的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接