【问题标题】:What is the preferred (performant and readable) way of chaining IEnumerable<T> extension methods?链接 IEnumerable<T> 扩展方法的首选(高性能和可读)方式是什么?
【发布时间】:2012-04-09 19:57:37
【问题描述】:

如果我尝试在 IEnumerable&lt;T&gt; 对象图的多个级别过滤结果,是否有首选的链接扩展方法的方法来执行此操作?

我对任何扩展方法和 lambda 用法持开放态度,但我不希望使用 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; }
}

【问题讨论】:

  • 可读性似乎差不多。性能取决于这是对对象的 linq 还是对其他任何东西的 linq;你为什么不测量一下看看?
  • 你的查询总是以libraries =&gt; books =&gt; chapters 的形式 - 或者你可能有更复杂的关系(我猜这只是一个测试模型)
  • @NSGaga 他们总是采用这种分层形式,但你说得对,它只是一个测试模型,让读者更容易理解。从技术上讲,我正在使用 EDI (ANSI X12) 结构,因此文件、ISA、GS 和 ST 段。
  • @phoog 我同意测量它是一个好主意,但是在此处的示例和我正在处理的实际代码中,我没有任何迹象表明我的数据可能会发生多大的变化。在一种情况下,我将拥有1 library3 books,每个5000 chapters。下一次我可能有1 library5000 books 每个1 chapter。因为我无法准确预测其中之一,所以测量并不理想,我希望获得一般指导,让可读性成为更核心的目标。
  • 我认为使用 LINQ 语法会使这样的代码更具可读性。而且我认为牺牲一致性是值得的。

标签: c# .net extension-methods ienumerable


【解决方案1】:

哪个最有可能因您使用的 LINQ 实现而异。 LinqToSql 的行为与内存过滤不同。子句的顺序应该会影响性能,具体取决于使用的数据,因为幼稚的实现会在序列的前面过滤更多的记录,这意味着后面的方法的工作量会减少。

对于您的两个示例,我猜性能差异可以忽略不计,并且会支持第一个示例,因为它可以更轻松地修改每个子句,而独立于其他子句。

至于确定最佳选项,它与其他任何事情相同:衡量。

【讨论】:

    【解决方案2】:

    我猜你的第一个表达式会稍微快一点,但速度并不明显。要真正确定其中一个是否更快,您需要使用分析器或秒表对它们进行计时。

    无论哪种方式,可读性似乎都没有受到很大影响。我更喜欢第一种方法,因为它的嵌套级别更少。这完全取决于您的个人喜好。

    【讨论】:

    • 是的,第一个会更快,因为您没有为每个项目创建 where-iterator。
    【解决方案3】:

    这取决于底层 LINQ 提供程序的工作方式。对于 LINQ to Objects,在这种情况下,两者都需要大约相同数量的工作,或多或少。但这是最直接(最简单)的例子,除此之外很难说。

    【讨论】:

      【解决方案4】:

      这可能会给你一个不同的角度,虽然这更多的是风格问题...
      我有时会发现自己在做这样的事情......

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

      ...在那里您可以猜到扩展名是什么,例如...

      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”等,而且对我来说不是那么可读(或者)。 虽然使用“短格式”,但很清楚哪个是哪个、在哪里、选择等,而且它非常“速记”,而且字符少得多。

      此外,您可以将关于 Where/Select 组合的决定推迟到以后(根据需要,提供者做一个或另一个)

      @Telastyn 说的很对,LINQ 提供者,例如如果您查看一些实现代码,
      所有表达式都减少等。
      是非常不确定的(即从提供者到提供者),它们最终可能会映射到例如SQL
      虽然这在我认为的大多数情况下应该是相同的。

      【讨论】:

        猜你喜欢
        • 2011-10-17
        • 1970-01-01
        • 2017-01-21
        • 1970-01-01
        • 2019-03-17
        • 2011-11-06
        • 2013-07-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多