我在理解C#中的GroupJoin时遇到了一些问题,想知道是否有人可以澄清一下。
假设我有这个对象的两个列表
public class Item
{
public int Id { get; set;}
public decimal Amount { get; set;}
}
var itemListOne = new List<Item>(){
new Item{
Id = 1,
Amount = 100
},
new Item{
Id = 2,
Amount = 200
},
new Item{
Id = 3,
Amount = 300
},
}
var itemListTwo = new List<Item>(){
new Item{
Id = 1,
Amount = 400
},
new Item{
Id = 2,
Amount = 500
}
}
然后调用一个LINQ查询,如下所示
var result = (from listOneItem in itemListOne
join listTwoItem in itemListTwo on listOneItem.Id equals listTwoItem.id into joinedItems
from joinedItem in joinedItems.DefaultIfEmpty()
select joinedItem != null ? joinedItem.Amount : 0).ToArray();
我在这段代码中寻找的是连接两个列表的Id字段。一旦连接,我将从第二个列表中选择金额值,而第一个列表中的剩余条目将不会连接,因此它将返回0到列表中。
这段代码实际上是有效的。当我测试它的时候,我可以看到前两个条目是400和500。第三个条目是0,因为第一个列表中的第三个条目不能加入第二个列表。但我不明白为什么它有效。
我阅读到GroupJoins,我想它提到返回值将是外部列表中的条目。所以我希望有一个100,200和300的数组,因为外部列表是itemListOne,对吗?但看起来它实际上给了我第二个列表中的值。
2条答案
按热度按时间iklwldmw1#
您的Linq表达式...
...相当于这样:
注意
i1
对象是如何传递到GroupJoin
的resultSelector
中的,resultSelector
将它们输出到一个新的匿名类型对象中,然后SelectMany
将其展平。一旦加入,我将从第二个项目列表中选择
Amount
,第一个列表中的剩余条目将不会加入,因此它将返回0到列表中。它会的,除了你使用的是
.DefaultIfEmpty()
,所以对于最后一项,表达式i2Items.DefaultIfEmpty()
的计算结果是null
,而不是空的IEnumerable<Item>
。因此,如果删除
.DefaultIfEmpty
和??
位,它仍然可以工作,但只输出2个decimal
值,而不是3个。通过这种简化,以下是每个Linq步骤之后的中间结果:
GroupJoin
:SelectMany
:在
GroupJoin
匹配完成后,似乎没有使用任何i1
对象,您可以通过从GroupJoin
的输出中省略i1
来进一步简化它:...并将
.Select( i2 => i2.Amount )
向上移动到resultSelector:
s8vozzvw2#
这个查询基本上表示了SQL left join的等价物,其中
joinedItem
表示第二个(右)连接部分,所以实际上会得到null
(因此需要空检查-joinedItem != null
)join(Id == 3
)的(左)部分。如果你想访问左部分的数据-使用listOneItem
。例如对于LINQ-to-Objects:将导致以下结果:
| 身份证|左金额|RightAmount|
| --------------|--------------|--------------|
| 1|一百|四百|
| 二|两百|五百|
| 三|三百|0|