ReSharperでLinq変換
ReSharperとはReSharperとは、Visual Studio のアドインで下記の機能があります。
・コーディング支援
・リファクタリング支援
・ユニット・テスト
・その他、リッチな機能がいろいろ
最新版は、Ver7.1.3となり、VisualStudio2012に対応しています。
一度使うと中毒になり、ReSharper畜となり、無いと不安に・・・・・かも知れません。
Linqへの変換リファクタリング機能の一つとして、foreachのループ文をクリック一発でLinqに変換する機能が有ります。
変換可能なループ文があると、foreachの下の波線と欄外に黄色いランプが表示されます。
ランプを右クリックすると操作可能なメニューが表示されます。
「Convert into LINQ-Expression」を選択すると、Where()に変換されているのが分かります。
暗号化?
次のコードは、BlackJumboDogの断片なのですが、ReSharperの機能でLingに変換すると、下記のようになりました。
public string Sample_002() { var log = ""; var db = new List<OneRR>(); foreach (var oneRr in db) { if (oneRr.DnsType == 1) { if (!oneRr.IsEffective) continue; if (oneRr.Name.ToUpper() == "ABC") { var find = false; foreach (var o in db) { if (o.Data == oneRr.Data) { find = true; break; } } if (!find) { log="Success data search."; } } } } return log; }
public string Sample_002() { var log = ""; var db = new List<OneRR>(); foreach (var find in from oneRr in db where oneRr.DnsType == 1 where oneRr.IsEffective where oneRr.Name.ToUpper() == "ABC" select db.Any(o => o.Data == oneRr.Data) into find where !find select find){ log="Success data search."; } return log; }
リファクタリングというより、暗号化?かもしれませんw
変換ロジックReSharperのLinq変換が、どのようなロジックで動作しているのか、少しでも分からないかと考え、Linqの機能ごとに変換の様子を確認してみました。
// 以降のサンプルは、下のクラス変数を対象にしています
private readonly int _list = new { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
1.【判定】All,Any,Containsなど
変換前public void 条件を満たす要素が含まれているか_Any() { var flg=false; foreach (var i in _list){ if (i > 5){ flg = true; break; } } } public void すべての要素が条件を満たしてるか_All() { var flg = true; foreach (var i in _list) { if (i > 5) { flg=false; break; } } } public void 指定要素が含まれているかどうか_Contains() { var flg = false; foreach (var i in _list) { if (i == 5) { flg = true; break; } } }
変換後
public void 条件を満たす要素が含まれているか_Any() { var flg = _list.Any(i => i > 5); } public void すべての要素が条件を満たしてるか_All() { var flg = _list.All(i => i <= 5); } public void 指定要素が含まれているかどうか_Contains() { var flg = _list.Any(i => i == 5); //Containsの方が、コードの意味はこっち //flg = _list.Contains(5); }
一瞬、うまく変換している!と思うのですが、よく見てみると、AnyとAllの違いは、元のコードがfalseのフラグをtrueにしてループを抜けるか、trueのフラグをfalseにして抜けるかの違いなので、コードの文意を判断しているわけでは無いようです。
その証拠に、文意がContains()だとしても、フラグが具合だけでAnyかAllになっています。
2.【集計】Max,Min,Average,Sum,Countなど
変換前public void 要素のカウント_count() { var count = 0; foreach (var i in _list){ count++; } } public void 要素の合計_Sum() { var sum = 0; foreach (var i in _list) { sum+=i; } } public void 最大値_Max() { var max = 0; foreach (var i in _list) { if (max < i) { max = i; } } }
変換後
public void 要素のカウント_count() { var count = _list.Count(); } public void 要素の合計_Sum() { var sum = _list.Sum(); } public void 最大値_Max() { var max = _list.Concat(new[]{0}).Max(); //これでいいんじゃ無いだろうか・・・ //max = _list.Max(); }
ちょっとMaxだけが微妙でしたが、大体いい感じです。
3.【複数の要素取得】Where,Distinct,Skip,Takeなど
変換後public void 条件を満たす要素の抽出_where() { var res = new List<int>(); foreach (var i in _list) { if (i > 5) { res.Add(i); } } } public void 先頭から3つをスキップ() { var res = new List<int>(); var count = 0; foreach (var i in _list) { if (count++ < 3) { continue; } res.Add(i); } } public void 先頭から3つだけを取得() { var res = new List<int>(); var count = 0; foreach (var i in _list) { if (count++ < 3) { break; } res.Add(i); } }
変換後
public void 条件を満たす要素の抽出_where() { var res = _list.Where(i => i > 5).ToList(); } public void 先頭から3つをスキップ() { var count = 0; var res = _list.Where(i => count++ >= 3).ToList(); //これでいいんじゃ無いだろうか・・・ //res = _list.Skip(3).ToList(); } public void 先頭から3つだけを取得() { var count = 0; var res = _list.TakeWhile(i => count++ >= 3).ToList(); //これでいいんじゃ無いだろうか・・・ //res = _list.Take(3).ToList(); }
SkipとかTakeになってほしいところも、全部Where、TakeWhileになってしまいます。
4.【単一の要素取得】First,Last,Single,ElementAtなど
変換前
public void 指定インデックの値取得(){ int c = 0; int res; foreach (var i in _list) { if (5==c++) { res = i; break; } } } public void 最初の値取得() { int res; int c = 0; foreach (var i in _list) { if (c == 0) { res = i; break; } } } public void 最後の値取得() { int res = -1; int c = 0; foreach (var i in _list) { if (c++ == _list.Count() - 1){ res = i; break; } } }
変換後
public void 指定インデックの値取得(){ int c = 0; int res; foreach (var i in _list.Where(i => 5==c++)){ res = i; break; } //これでいいんじゃ無いだろうか・・・ //res = _list.ElementAt(5); } public void 最初の値取得() { int res; int c = 0; foreach (var i in _list.Where(i => c == 0)){ res = i; break; } //これでいいんじゃ無いだろうか・・・ //res = _list.First(); } public void 最後の値取得() { int res = -1; int c = 0; foreach (var i in _list.Where(i => c++ == _list.Count() - 1)){ res = i; break; } //これでいいんじゃ無いだろうか・・・ //res = _list.Last(); }
こちらは、全滅です。
どうしても、Where+foreachになってしまいます・・・・・
5.【その他】
その他として・・・・・「集合」 Union(和集合)Except(差集合) Intersect(積集合)
「ソート」 OrderBy(昇順ソート)Reverse(降順ソート)
「グルーピング」 GroupBy(グルーピング)
「結合」 Join(内部結合)Concat(連結)Zip(マージ)
なんとか、上記に変換されるループを作ろうと色々試してみたのですが、殆どwhere/select・Any/Allあたり変換されてしまって、結局うまく行きませんでした。
私のループ分がショボイのか、ReSharperの機能がそういう事なのか・・・・ちょっと不明です。
結論結論というか、まったく私の勝手なイメージなんですが・・・
なんとなく、殆どの変換はAny,All,Where,selectぐらい解決してしまっているような気がしてきました。そういうイメージで、最初の暗号化?されたLinqを眺めてみると、なるほど・・・って感じもしてきます。
すいません、ほんと、私の勝手な結論です。
根拠は一切ありません。