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.NETとC#でデザインパターン
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();