2012-04-09 6 views
6

IEnumerable<T>オブジェクトグラフの複数のレベルで結果をフィルタリングしようとしている場合、これを行うための拡張メソッドをチェーンするのが好ましい方法はありますか?拡張メソッドのIEnumerable <T>をチェーン化するための好ましい(実行可能で可読な)方法は何ですか?

私は任意の拡張メソッドとラムダの使用法を公開していますが、残りのコードベースとの一貫性を保つためにLINQ構文を使用しないことをお勧めします。

SelectMany()メソッドのselectorにフィルタリングをプッシュするか、別のWhere()メソッドをチェーンするほうがよいですか?または、より良い解決策がありますか?

最適なオプションを特定するにはどうすればよいですか?このテストケースでは、すべてがメモリ内で直接利用可能です。明らかに、以下の両方のサンプルは、現在同じ正しい結果を生成しています。私はちょうど理由を探している、または他の(または別のオプション)が優先されるだろう。

public class Test 
{ 
    // I want the first chapter of a book that's exactly 42 pages, written by 
    // an author whose name is Adams, from a library in London. 
    public Chapter TestingIEnumerableTExtensionMethods() 
    { 
     List<Library> libraries = GetLibraries(); 

     Chapter chapter = libraries 
      .Where(lib => lib.City == "London") 
      .SelectMany(lib => lib.Books) 
      .Where(b => b.Author == "Adams") 
      .SelectMany(b => b.Chapters) 
      .First(c => c.NumberOfPages == 42); 

     Chapter chapter2 = libraries 
      .Where(lib => lib.City == "London") 
      .SelectMany(lib => lib.Books.Where(b => b.Author == "Adams")) 
      .SelectMany(b => b.Chapters.Where(c => c.NumberOfPages == 42)) 
      .First(); 
    } 

そして、ここでサンプルオブジェクトグラフです:

public class Library 
{ 
    public string Name { get; set; } 
    public string City { get; set; } 
    public List<Book> Books { get; set; } 
} 

public class Book 
{ 
    public string Name { get; set; } 
    public string Author { get; set; } 
    public List<Chapter> Chapters { get; set; } 
} 

public class Chapter 
{ 
    public string Name { get; set; } 
    public int NumberOfPages { get; set; } 
} 
+1

可読性はほぼ同じです。パフォーマンスは、これがオブジェクトのlinqか他のもののlinqかによって異なります。なぜあなたはそれを測定して見ないのですか? – phoog

+0

はいつも 'libraries => books => chapters'の形式であなたの問い合わせです - あるいはもっと複雑な関係を持つことができます(これは単なるテストモデルです) – NSGaga

+0

@NSGagaそれらは常にその階層形式です。読者が理解しやすくするためのテストモデルに過ぎないということは間違いありません。技術的には、EDI(ANSI X12)構造、ファイル、ISA、GS、STセグメントを扱っています。 –

答えて

1

それは根本的なLINQプロバイダがどのように機能するかに依存します。 LINQ to Objectsの場合、どちらの場合もほぼ同じ量の作業が必要になります。しかし、それは最も簡単な(最も単純な)例です。それを超えると、言うことは難しいです。

2

私は、最初の式はわずかながらはるかに重要ではないと思っています。どちらか一方がより高速かどうかを実際に判断するには、プロファイラまたはストップウォッチを使って時間を計測する必要があります。

読みやすさに大きな影響を与えないようです。私はネストのレベルが低いので、最初のアプローチを好む。それはあなたの個人的な好みによって異なります。

+0

ええ、最初のアイテムは、各アイテムのイテレータを作成していないため、処理が速くなります。 – usr

3

これは、使用しているLINQの実装によって異なる場合があります。 LinqToSqlはメモリ内のフィルタリングとは動作が異なります。単純な実装ではシーケンスの早い段階でより多くのレコードがフィルタリングされ、後のメソッドの処理が少なくなるため、句の順序はどのデータが使用されているかによってパフォーマンスに影響を与えるはずです。

あなたの2つの例では、パフォーマンスの差はごくわずかで、他のものとは無関係に各節を簡単に変更できるため、最初の方が有利です。

最良のオプションを判断するには、他のものと同じです。測定値。私は時々自分自身がこのような何かをやって見つける

0

それはより多くのスタイルの問題だが、これは、あなたに別の角度を与えるかもしれない... ...

return libraries.Filter(
     l => l.City == "", 
     l => l.Books, 
     b => b.Author == "Adams", 
     b => b.Chapters, 
     c => c.NumberOfPages == 42 
     ); 

...あなたは何を推測することができますextensiionは、何かのような...

public static IEnumerable<TC> Filter<TL, TB, TC>(this IEnumerable<TL> list, 
    Func<TL, bool> whereLs, 
    Func<TL, IEnumerable<TB>> selectBs, 
    Func<TB, bool> whereBs, 
    Func<TB, IEnumerable<TC>> selectCs, 
    Func<TC, bool> whereCs 
    ) 
{ 
    return list 
     .Where(whereLs) 
     .SelectMany(selectBs) 
     .Where(whereBs) 
     .SelectMany(selectCs) 
     .Where(whereCs); 
} 

...または....

...  
{ 
    return list 
     .Where(whereLs) 
     .SelectMany(l => selectBs(l).Where(whereBs)) 
     .SelectMany(b => selectCs(b).Where(whereCs)); 
} 
です

そして、組み合わせ/オプションは、あなたが持っているもの、あなたのコードをどうやって持っているかによって(多くを抽象化したり、「キャプチャ」、「パラメータ化」など、多くのものがあります。 PerCityAuthorPages(_city, _author, _numPages);など)

...基本的に、私は 'Where'、 'Select'sなどのすべてを嫌い、私にはそれを読めるものではありません。 「短い形式」では、どちらがどのような場所で選択されているかがはっきりしていますが、それは非常に短期間で、はるかに少ない文字であります。

また、例えば、あなたが

(ニーズ、プロバイダに基づいて1つまたはその他を行う)以降ではどこに/選択の組み合わせについての決定をdefferことができ、@Telastynがかなり右である、LINQプロバイダいくつかの実装コードを見ると、すべての式が縮小されている
などが縮小されています。
は、結局、例えば、プロバイダからプロバイダへとかなり非決定的です。 SQL
これはほとんど私が思うに同じマップする必要があります。

関連する問題