LINQ-言語統合クエリの楽しさを楽しむために、C#でどれだけ発明されたか。つまり:- ジェネリック
- 拡張メソッド
- ラムダ式
- 式の木
- Anonumusタイプ
- オブジェクト初期化子
- 型推論
これで、次のようなものを書くことができます。 var query =
from itemA in listA
join itemB in listB
on itemA.Key equals itemB.Key
select new {itemA, itemB};
人は反対することはできません-印象的。そして、このすべての構文糖の真ん中に、私が十分な睡眠をとることを妨げる露のスプーンがありました:)これはOUTER JOINのサポートの完全な欠如です。しかし、それが判明したので、タールは簡単に変わります...ターン...ターン......別の構文糖に。インターネットで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};
そのような設計は、桁違いに理解を明らかに混乱させ、すでに単純な構造を複雑にします。これは、INNER JOINをLEFT OUTER 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クエリは自由にコンパイルされます。標準のIEnumerableインターフェイスを実装するLINQで変数を使用する場合、拡張メソッドが使用されます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を何らかの方法で制御可能な型に変換する必要があります。次の2つのクラスを実装することにより: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);
}
Voila ...美しいLEFT OUTER JOIN: 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にグッズを実装するための興味深いソリューションがいくつかあります。それらを共有する時間があるでしょう。ご清聴ありがとうございました。POWERがあなたと一緒にいられるように!