Сначала он эффективно создает поиск из «внутренней» последовательности, затем перебирает внешнюю последовательность. Затем он может искать каждый ключ из внешней последовательности и выводить каждую подходящую пару. Примерно так (игнорирование проверки аргументов и т. Д.):
public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>
(this IEnumerable<TOuter> outer,
IEnumerable<TInner> inner,
Func<TOuter, TKey> outerKeySelector,
Func<TInner, TKey> innerKeySelector,
Func<TOuter, TInner, TResult> resultSelector)
{
Lookup<TKey, TInner> lookup = inner.ToLookup(innerKeySelector);
foreach (TOuter outerItem in outer)
{
TKey key = outerKeySelector(outerItem);
foreach (TInner innerItem in lookup[key])
{
yield return resultSelector(outerItem, innerItem);
}
}
}
При поиске для ключей будет использоваться внутренняя хеш-таблица, так что эффективно искать любой отдельный ключ.