答案
Does p refer to products after into keyword?
子句中的 pfrom
是一个新的局部变量,指的是一个类别的单个产品。
Is ps the group of product objects? I mean a sequence of sequences.
是的,ps
是该类别的产品组c
。但它不是一个序列的序列,只是一个简单的IEnumerable<Product>
,就像c
是一个单一的类别,并不是组中的所有类别都加入。
在查询中,您只能看到一个结果行的数据,而不是整个组的连接结果。看看 final select
,它打印了一个类别和一个它加入的产品。该产品来自一个类别加入ps
的产品组。
然后,查询会遍历所有类别及其所有产品组。
If DefaultIfEmpty() isn't used, doesn't p, from p in ps.DefaultIfEmpty(), run into select? Why?
它不等于 a Select
,因为该from
子句与自身创建了一个新的连接,它变成了SelectMany
。
结构
分部分查询,首先加入组:
from c in categories
join p in products on c equals p.Category into ps
在此之后才可c
用ps
,代表一个类别及其加入的产品。
现在请注意,整个查询与以下形式相同:
from car in Cars
from passenger in car.Passengers
select (car, passenger)
它Cars
与自己的Passengers
使用相结合Cars.SelectMany(car => car.Passengers, (car, passenger) => (car, passenger));
所以在你的查询中
from group_join_result into ps
from p in ps.DefaultIfEmpty()
使用 SelectMany 通过 DefaultIfEmpty 使用其自己的数据(分组产品列表)创建先前组连接结果的新连接。
结论
最后,复杂性在于 Linq 查询而不是 DefaultIfEmpty 方法。我在评论中发布的 MSDN 页面上简单地解释了该方法。它只是将没有元素的集合转换为具有 1 个元素的集合,该元素是 default() 值或提供的值。
编译源
这大约是查询编译为的 C# 代码:
//Pairs of: (category, the products that joined with the category)
IEnumerable<(string category, IEnumerable<Product> groupedProducts)> groupJoinData = Enumerable.GroupJoin(
categories,
products,
(string c) => c,
(Product p) => p.Category,
(string c, IEnumerable<Product> ps) => (c, ps)
);
//Flattening of the pair collection, calling DefaultIfEmpty on each joined group of products
IEnumerable<(string Category, string ProductName)> q = groupJoinData.SelectMany(
catProdsPair => catProdsPair.groupedProducts.DefaultIfEmpty(),
(catProdsPair, p) => (catProdsPair.category, (p == null) ? "(No products)" : p.ProductName)
);
在 ILSpy 的帮助下使用 C# 8.0 视图完成。