上一章我們講到Join
的應用方式,在方法中設定inner
跟outer
及對應的鍵值就可以取得兩個資料(物件)合併的資料,現在我們來看看他是怎麼做到的吧。
Source Code: Join.cs
Join
有兩個公開方法,差別在於其中一個多了一個Comparer
的參數,而這兩個公開方法的實作其實就只差在這個Comparer
有沒有傳入Iterator
而已,下面列出了他們的實作流程:
ArgumentNull
例外JoinIterator
取得Join的結果接下來我們來看JoinIterator
:
private static IEnumerable<TResult> JoinIterator<TOuter, TInner, TKey, TResult>(IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, IEqualityComparer<TKey> comparer)
{
using (IEnumerator<TOuter> e = outer.GetEnumerator())
{
if (e.MoveNext())
{
Lookup<TKey, TInner> lookup = Lookup<TKey, TInner>.CreateForJoin(inner, innerKeySelector, comparer);
if (lookup.Count != 0)
{
do
{
TOuter item = e.Current;
Grouping<TKey, TInner> g = lookup.GetGrouping(outerKeySelector(item), create: false);
if (g != null)
{
int count = g._count;
TInner[] elements = g._elements;
for (int i = 0; i != count; ++i)
{
yield return resultSelector(item, elements[i]);
}
}
}
while (e.MoveNext());
}
}
}
}
此Iterator
的流程如下:
outer
的每個元素inner
鍵值分組的inner
元素的Grouping
outer
的鍵值去inner
的Grouping
中查找是否有相同的鍵值組別outer
及inner
傳給resultSelector
取得結果回傳這裡我們看到了一個熟悉的身影,就是上次介紹GroupBy
的時候有講解的Lookup
,它的功用是可以將相同鍵值的物件整理到同一個Grouping
物件中,這裡它將inner
分組,outer
再使用它查找對應的鍵值,藉以取得對應的資料。
這裡也可以看出因為外層是巡覽outer
,outer
找到inner
後才依序輸出outer
跟inner
的資料,所以資料排序會是outer
後才是inner
。
最後這段實作告訴我們Join
這個方法確實是Inner Join的實作,原因可以從inner
跟outer
取值的方式知道:
inner
依鍵值分組outer
依inner
組別取得對應的資料從取值的方式可以知道其中一方沒有值是都不會成為結果的。
[Fact]
public void SelectorsReturnNull()
{
int?[] inner = { null, null, null };
int?[] outer = { null, null };
Assert.Empty(outer.Join(inner, e => e, e => e, (x, y) => x));
}
如果是null
跟null
做Join
的話,還是會得到空。
這篇的篇幅比較短,真正複雜的分組(Lookup
)已經在介紹GroupBy
的時候講解了,這裡就是利用Lookup
來取得對應的資料,明天我們來介紹另一個Join
: GroupJoin
。