IDisposableインターフェースを実装したクラスを作るときに気を付けること。

IDisposableインターフェースを実装したクラスは、usingステートメントを使って確実な終了処理が出来ないといけない。

usingステートメントは以下のように書き

using (var db = new SqlConnection(connectionString))
{
    db.Open();
}

usingステートメントを使わない以下のコードと同等になる

var db = new SqlConnection(connectionString);
try
{
    db.Open();
}
finally
{
    if (db != null)
    {
        ((IDisposable)db).Dispose();
    }
}

ポイントは、tryの前にオブジェクトの生成が行われている点。
つまり、コンストラクタで例外が発生した場合Dispose()が実行されないということ。

■結論
というわけで、コンストラクタで例外が発生した場合はDispose()が不要な様に作る必要がある。


■追記
よく考えるとusingステートメントとか関係ないか。
コンストラクタで例外が発生したら戻り値が無いからDispose()呼びようが無い。
リソース開放が必要なクラスのコンストラクタ全般に言える話でした。

グループ化して上位N件ずつ取得

似たような事を既に書いてるけど、懲りずに。

■やりたいこと:
テーブル(コレクション)TからGでグルーピングしてOで並べ替えて、グループ毎の上位N件ずつだけ取得。

T-SQLだとこんな感じか。

SELECT *
FROM (
    SELECT *, ROW_NUMBER() OVER (PARTITION BY G ORDER BY O) rn
    FROM T) x
WHERE rn <= N


LINQ to Objects だとこう

T.GroupBy(x => x.G).Select(x => x.OrderBy(y => y.O).Take(N)).SelectMany(x => x);


メソッド化

public static IEnumerable<T> TopPerGroup<T, TGroupKey, TOrderKey>(
    this IEnumerable<T> source,
    Func<T, TGroupKey> groupKeySelector,
    Func<T, TOrderKey> orderKeySelector,
    int count)
{
    return source
        .GroupBy(groupSelector)
        .Select(x => x.OrderBy(orderKeySelector).Take(count))
        .SelectMany(x => x);
}


使い方

T.TopPerGroup(x => x.G, x => x.O, N);

集合論とかの演算をC#(LINQ)でどう書くか考えてみた。

とりあえず 数学記号の表 - Wikipedia に載ってる項目について。

■論理演算・量化

論理積 P ∧ Q P && Q
論理和 P ∨ Q P || Q
否定 ¬P !P
全称量化 ∀x ∈ S; P(x) S.All(x => P(x))
存在量化 ∃ x ∈ S; P(x) S.Any(x => P(x))
存在量化 ∃x(x ∈ S) S.Any()


■集合間の関係

集合の要素 x ∈ S S.Contains(x)
集合の要素の否定 x ∉ S !S.Contains(x)
集合の一致 S = T !S.Exept(T).Any() && !T.Exept(S).Any()
集合の一致の否定 S ≠ T S.Exept(T).Any() || T.Exept(S).Any()
部分集合 S ⊆ T !S.Exept(T).Any()
真部分集合 S ⊂ T !S.Exept(T).Any() && T.Exept(S).Any()
部分集合の否定 S ⊄ T S.Exept(T).Any()


■集合演算

※1 {x ∈ S : P(x)} S.Where(x => P(x))
積集合 S ∩ T S.Intersect(T)
和集合 S ∪ T S.Union(T)
差集合 S \ T S.Except(T)
順序対 (x, y, …) Tuple.Create(x, y, ...)
直積集合 S × T S.SelectMany(x => T.Select(y => Tuple.Create(x, y)))
商集合 S/~ S.GroupBy(x => ~(x))
写像の全体 Map(S, T) S.Select(x => f(x))
対象差 S△T S.Exept(T).Union(T.Exept(S))

※1)ある命題を満たす元を集めた集合

※)P,Qは命題。S,Tは集合

デザインパターン

デザインパターン勉強しないとなぁ。

@IT - 連載:[完全版]究極のC#プログラミング
C#3.0 デザインパターン ミニカタログ
http://www.atmarkit.co.jp/fdotnet/extremecs/extremecs_20/extremecs_20_02.html

@IT - .NETで始めるデザインパターン
http://www.atmarkit.co.jp/fdotnet/designptn/index/index.html

■かずきのBlog@Hatena
C# 4.0で実装するデザインパターン「その1 生成に関するパターン」
http://d.hatena.ne.jp/okazuki/20110103/1294028317

VB.NETC#デザインパターン
http://hccweb1.bai.ne.jp/tsune-1/

■Rarestyleへようこそ - デザインパターン
http://www.rarestyle.net/main/patterns/patterns.aspx

■Gushwell's C# Programming Page - C#デザインパターン
http://gushwell.ifdef.jp/dp/csdpindex.html

■TECHSCORE - デザインパターン
http://www.techscore.com/tech/DesignPattern/index.html/

MSDN - Code Recipe
連載! とことん C#: 第 1 回 言語の特性とパターン (鵜呑みは厳禁、Java のデザイン・パターン!)
http://code.msdn.microsoft.com/1-Java-44175826/

MSDN - .NET Framework で使用されているデザイン パターンを見つける
http://msdn.microsoft.com/ja-jp/magazine/dd764263.aspx

MSDN - 非同期プログラミングのデザイン パターン
http://msdn.microsoft.com/ja-jp/library/ms228969(v=vs.100).aspx

■Scribbled Records - C#デザインパターン - まとめ
http://msyi303.blog130.fc2.com/blog-entry-52.html

■Yohta's Object World - Skeleton of GOF's Design Patterns
http://www002.upp.so-net.ne.jp/ys_oota/mdp/

SQLアンチパターン - 開発者を待ち受ける25の落とし穴 (拡大版)
http://www.slideshare.net/t_wada/sql-antipatterns-digest

■Ynishi Bussiness Logs - 2013-04-26 SQLアンチパターン1
http://d.hatena.ne.jp/yuuntim/20130426

グループ化してセット演算子を適用する。 4

さらに再考。
ILookupに変換する部分を抽出。これでもうすこし使い勝手が良くなる。

■グループ化セット演算

public static class GroupLinqExtensions
{
  // groupのシーケンスをlookupに変換
  public static ILookup<TKey, T> GroupToLookup<T, TKey>(this IEnumerable<IGrouping<TKey, T>> source)
  {
    return source.SelectMany(x => x.Select(y => new { key = x.Key, value = y })).ToLookup(x => x.key, x => x.value);
  }

  // グループ化したキーで重複除去
  public static IEnumerable<T> GroupDistinct<T, TKey>(this IEnumerable<IGrouping<TKey, T>> source)
  {
    return source.GroupToLookup().Select(x => x.First());
  }

  // グループ化したキーで和
  public static IEnumerable<IGrouping<TKey, T>> GroupUnion<T, TKey>(this IEnumerable<IGrouping<TKey, T>> first, IEnumerable<IGrouping<TKey, T>> second)
  {
    var unionedKeys = first.Select(x => x.Key).Union(second.Select(x => x.Key));
    return first.Union(second).Where(x => unionedKeys.Contains(x.Key));
  }

  // グループ化したキーで積
  public static IEnumerable<IGrouping<TKey, T>> GroupIntersect<T, TKey>(this IEnumerable<IGrouping<TKey, T>> first, IEnumerable<IGrouping<TKey, T>> second, bool onlyFirstSource = false)
  {
    var intersectedKeys = first.Select(x => x.Key).Intersect(second.Select(x => x.Key));
    var srcGroup = onlyFirstSource ? first : first.Union(second);
    return srcGroup.Where(x => intersectedKeys.Contains(x.Key));
  }

  // グループ化したキーで差
  public static IEnumerable<IGrouping<TKey, T>> GroupExcept<T, TKey>(this IEnumerable<IGrouping<TKey, T>> first, IEnumerable<IGrouping<TKey, T>> second)
  {
    var exceptedKeys = first.Select(x => x.Key).Except(second.Select(x => x.Key));
    return first.Where(x => exceptedKeys.Contains(x.Key));
  }
}

グループ化してセット演算子を適用する。 3

さらに再考。
メソッドチェーンを繋げていくことを考えると、IEnumerable<IGrouping> を受け取って IEnumerable<IGrouping> を返すのが良い。

■グループ化セット演算

public static class LinqExtensions
{
  // グループ化したキーで重複除去
  public static IEnumerable<T> DistinctByKey<T, TKey>(this IEnumerable<IGrouping<TKey, T>> source)
  {
    return source.SelectMany(x => x.Select(y => new { key = x.Key, value = y })).ToLookup(x => x.key, x => x.value).Select(x => x.First());
  }

  // グループ化したキーで和
  public static IEnumerable<IGrouping<TKey, T>> GroupUnion<T, TKey>(this IEnumerable<IGrouping<TKey, T>> g1, IEnumerable<IGrouping<TKey, T>> g2)
  {
    var unionedKeys = g1.Select(x => x.Key).Union(g2.Select(x => x.Key));
    return g1.Union(g2).Where(x => unionedKeys.Contains(x.Key));
  }

  // グループ化したキーで積
  public static IEnumerable<IGrouping<TKey, T>> GroupIntersect<T, TKey>(this IEnumerable<IGrouping<TKey, T>> g1, IEnumerable<IGrouping<TKey, T>> g2, bool onlyFirstSource = false)
  {
    var intersectedKeys = g1.Select(x => x.Key).Intersect(g2.Select(x => x.Key));
    var srcGroup = onlyFirstSource ? g1 : g1.Union(g2);
    return srcGroup.Where(x => intersectedKeys.Contains(x.Key));
  }

  // グループ化したキーで差
  public static IEnumerable<IGrouping<TKey, T>> GroupExcept<T, TKey>(this IEnumerable<IGrouping<TKey, T>> g1, IEnumerable<IGrouping<TKey, T>> g2)
  {
    var exceptedKeys = g1.Select(x => x.Key).Except(g2.Select(x => x.Key));
    return g1.Where(x => exceptedKeys.Contains(x.Key));
  }
}

■使い方

// a, b, c のシーケンスがある
var ag = a.GroupBy(x => x.Item);
var bg = b.GroupBy(x => x.Item);
var cg = c.GroupBy(x => x.Item);
var dest = ag.GroupExcept(bg).GroupExcept(cg).DistinctByKey();

グループ化してセット演算子を適用する。 2

再考。さっきよりは良い感じ。
Distinct は IEnumerable<IGrouping> を受け取って、他のセット演算は IEnumerable<IGrouping> を返すのがポイント。

■グループ化セット演算

public static class LinqExtensions
{
  // グループ化したキーで重複除去 (sourceはグループ化済)
  public static IEnumerable<T> DistinctByKey<T, TKey>(this IEnumerable<IGrouping<TKey, T>> source)
  {
    var lookup = source.SelectMany(x => x.Select(y => new { key = x.Key, value = y })).ToLookup(x => x.key, x => x.value);
    return lookup.Select(x => x.First());
  }

  // グループ化したキーで和
  public static IEnumerable<IGrouping<TKey, T>> GroupUnion<T, TKey>(this IEnumerable<T> first, Func<T, TKey> keySelector, IEnumerable<T> second)
  {
    var g1 = first.GroupBy(keySelector);
    var g2 = second.GroupBy(keySelector);
    var unionedKeys = g1.Select(x => x.Key).Union(g2.Select(x => x.Key));
    return g1.Union(g2).Where(x => unionedKeys.Contains(x.Key));
  }

  // グループ化したキーで積
  public static IEnumerable<IGrouping<TKey, T>> GroupIntersect<T, TKey>(this IEnumerable<T> first, Func<T, TKey> keySelector, IEnumerable<T> second, bool onlyFirstSource = false)
  {
    var g1 = first.GroupBy(keySelector);
    var g2 = second.GroupBy(keySelector);
    var intersectedKeys = g1.Select(x => x.Key).Intersect(g2.Select(x => x.Key));
    var srcGroup = onlyFirstSource ? g1 : g1.Union(g2);
    return srcGroup.Where(x => intersectedKeys.Contains(x.Key));
  }

  // グループ化したキーで差
  public static IEnumerable<IGrouping<TKey, T>> GroupExcept<T, TKey>(this IEnumerable<T> first, Func<T, TKey> keySelector, IEnumerable<T> second)
  {
    var g1 = first.GroupBy(keySelector);
    var g2 = second.GroupBy(keySelector);
    var exceptedKeys = g1.Select(x => x.Key).Except(g2.Select(x => x.Key));
    return g1.Where(x => exceptedKeys.Contains(x.Key));
  }
}

■使い方

// a, b のシーケンスがある
var distincted = a.GroupBy(x => x.Item).DistinctByKey();
var excepted = a.GroupExcept(x => x.Item, b).DistinctByKey();