LINQ-C#中已经发明了多少,以便我们可以享受语言集成查询的乐趣。即:- 泛型
- 扩展方式
- Lamda表达式
- 表达树
- 肛门型
- 对象初始化器
- 类型推断
就是这样,我们可以这样写: var query =
from itemA in listA
join itemB in listB
on itemA.Key equals itemB.Key
select new {itemA, itemB};
一个不能不同意-印象深刻。在所有这些语法糖中,有一汤匙的露水阻止了我足够的睡眠:)这完全是对OUTER JOIN的支持。但是事实证明,焦油很容易转动...转动...转动......成为另一种语法糖。那些试图在Internet上为LEFT OUTER JOIN找到解决方案的人,可能知道类似的解决方案: var query =
from itemA in listA
join itemB in listB
on itemA.Key equals itemB.Key into outer
from itemO in outer.DefaultIfEmpty()
select new {itemA, itemO};
这样的设计显然使理解变得混乱一个数量级,并使已经很简单的结构变得复杂。这只是用LEFT OUTER JOIN代替INNER JOIN的方法。为了不让您继续感到震惊,我不会举一个FULL OUTER JOIN的例子。如果我们可以这样写,看起来似乎很简单: var query =
from itemA in listA
left join itemB in listB
on itemA.Key equals itemB.Key
select new {itemA, itemB};
或者 var query =
from itemA in listA
full join itemB in listB
on itemA.Key equals itemB.Key
select new {itemA, itemB};
但不是。C#的作者没有为我们提供这种乐趣。好吧,没关系。但是,它们将允许我们自己执行此操作,尽管方法并非如此优美。首先,如果有人告诉你,LINQ和System.Collections.Generic.IEnumerable接口有共同的东西,不能单独存在,你可以放心地笑在人...设计 var query =
from itemA in listA
join itemB in listB
on itemA.Key equals itemB.Key
select new {itemA, itemB};
编译器将其简单地转换为以下字符序列: var query = listA.Join(listB, itemA => itemA.Key, itemB => itemB.Key, (itemA, itemB) => new {itemA, itemB});
变量listA和listB是什么类型都没有关系。假设listA是TypeA类型的变量,而可允许itemB是TypeB类型。因此,如果TypeA和TypeB包含一个称为Key的属性或字段,则TypeA包含具有4个参数的Join()方法。该LINQ查询可自由编译。在LINQ中使用实现标准IEnumerable接口的变量时,将使用扩展方法public class System.Linq.Enumerable
{
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) {...}
}
实际上,此方法还可以产生众所周知的INNER JOIN。现在街头魔术开始了。为了实现LEFT / RIGHT / FULL OUTER JOIN(或JOIN,这将使您的灵魂满意),您需要用我们实现的方法替换标准方法的调用。为此,我们需要以某种方式将变量listA转换为我们可以控制的类型。通过实现以下两个类:public class JoinedEnumerable<T> : IEnumerable<T>
{
public readonly IEnumerable<T> Source;
public bool IsOuter;
public JoinedEnumerable(IEnumerable<T> source) { Source = source; }
IEnumerator<T> IEnumerable<T>.GetEnumerator() { return Source.GetEnumerator(); }
IEnumerator IEnumerable.GetEnumerator() { return Source.GetEnumerator(); }
}
public static class JoinedEnumerable
{
public static JoinedEnumerable<TElement> Inner<TElement>(this IEnumerable<TElement> source)
{
return Wrap(source, false);
}
public static JoinedEnumerable<TElement> Outer<TElement>(this IEnumerable<TElement> source)
{
return Wrap(source, true);
}
public static JoinedEnumerable<TElement> Wrap(IEnumerable<TElement> source, bool isOuter)
{
JoinedEnumerable<TElement> joinedSource
= source as JoinedEnumerable<TElement> ??
new JoinedEnumerable<TElement>(source);
joinedSource.IsOuter = isOuter;
return joinedSource;
}
}
我们可以轻松编写下一个LINQ查询 var query =
from itemA in listA.Outer()
join itemB in listB
on itemA.Key equals itemB.Key
select new {itemA, itemB};
现在,按照我们所需的方式为JoinedEnumerable类实现Join扩展方法,我们将获得所需的一切。这是扩展方法:public static IEnumerable<TResult> Join<TOuter, TInner, TKey, TResult>(this JoinedEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, IEqualityComparer<TKey> comparer = null)
{
if (outer == null) throw new ArgumentNullException("outer");
if (inner == null) throw new ArgumentNullException("inner");
if (outerKeySelector == null) throw new ArgumentNullException("outerKeySelector");
if (innerKeySelector == null) throw new ArgumentNullException("innerKeySelector");
if (resultSelector == null) throw new ArgumentNullException("resultSelector");
bool leftOuter = outer.IsOuter;
bool rightOuter = (inner is JoinedEnumerable<TInner>) && ((JoinedEnumerable<TInner>)inner).IsOuter;
if (leftOuter && rightOuter)
return FullOuterJoin(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
if (leftOuter)
return LeftOuterJoin(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
if (rightOuter)
return RightOuterJoin(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
return Enumerable.Join(outer, inner, outerKeySelector, innerKeySelector, resultSelector, comparer);
}
public static IEnumerable<TResult> LeftOuterJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, IEqualityComparer<TKey> comparer = null)
{
var innerLookup = inner.ToLookup(innerKeySelector, comparer);
foreach (var outerItem in outer)
foreach (var innerItem in innerLookup[outerKeySelector(outerItem)].DefaultIfEmpty())
yield return resultSelector(outerItem, innerItem);
}
public static IEnumerable<TResult> RightOuterJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, IEqualityComparer<TKey> comparer = null)
{
var outerLookup = outer.ToLookup(outerKeySelector, comparer);
foreach (var innerItem in inner)
foreach (var outerItem in outerLookup[innerKeySelector(innerItem)].DefaultIfEmpty())
yield return resultSelector(outerItem, innerItem);
}
public static IEnumerable<TResult> FullOuterJoin<TOuter, TInner, TKey, TResult>(this IEnumerable<TOuter> outer, IEnumerable<TInner> inner, Func<TOuter, TKey> outerKeySelector, Func<TInner, TKey> innerKeySelector, Func<TOuter, TInner, TResult> resultSelector, IEqualityComparer<TKey> comparer = null)
{
var outerLookup = outer.ToLookup(outerKeySelector, comparer);
var innerLookup = inner.ToLookup(innerKeySelector, comparer);
foreach (var innerGrouping in innerLookup)
if (!outerLookup.Contains(innerGrouping.Key))
foreach (TInner innerItem in innerGrouping)
yield return resultSelector(default(TOuter), innerItem);
foreach (var outerGrouping in outerLookup)
foreach (var innerItem in innerLookup[outerGrouping.Key].DefaultIfEmpty())
foreach (var outerItem in outerGrouping)
yield return resultSelector(outerItem, innerItem);
}
瞧...美丽的左外联接: var query =
from itemA in listA.Outer()
join itemB in listB
on itemA.Key equals itemB.Key
select new {itemA, itemB};
美丽的右外连接: var query =
from itemA in listA.Inner()
join itemB in listB.Outer()
on itemA.Key equals itemB.Key
select new {itemA, itemB};
美丽的FULL OUTER JOIN: var query =
from itemA in listA.Outer()
join itemB in listB.Outer()
on itemA.Key equals itemB.Key
select new {itemA, itemB};
现在,如果您愿意,您可以使用自己的方法-因为这里的想象空间很大。在zashashnik中,我还有一些有趣的解决方案来实现好东西。会有时间分享它们。谢谢您的关注。愿权力与您同在!