链接IEnumerable <t>扩展方法的首选(高效和可读)方式是什么?</t>

时间:2012-04-09 19:57:37

标签: c# .net extension-methods ienumerable

如果我试图在IEnumerable<T>对象图的多个级别上过滤结果,是否有一种链接扩展方法的首选方法来执行此操作?

我对任何扩展方法和lambda用法持开放态度,但我不希望使用LINQ语法与代码库的其余部分保持一致。

将过滤推送到selector方法的SelectMany()或仅链接另一个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; }
}

4 个答案:

答案 0 :(得分:3)

哪种情况最好根据您使用的LINQ实施而有所不同。 LinqToSql的行为与内存中过滤不同。子句的顺序应该影响性能,具体取决于使用的数据,因为天真的实现将在序列中较早地过滤更多记录,这意味着后续方法的工作量减少。

对于你的两个例子,我猜想性能差异可以忽略不计,并且有利于第一个,因为它允许更容易地修改每个子句而不依赖于其他子句。

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

答案 1 :(得分:2)

我猜你的第一个表达会略微但速度微不足道。要真正确定一个或另一个是否更快,您需要使用分析器或秒表计时。

可读性似乎没有受到任何影响。我更喜欢第一种方法,因为它的嵌套水平较低。这完全取决于您的个人偏好。

答案 2 :(得分:1)

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

答案 3 :(得分:0)

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

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'等等,对我来说也不是那么可读(或者)。 虽然使用“简短形式”,但很明显哪个是哪个,哪里,哪个选择等等,而且它非常“短手”而且很少有字符。

此外,你可以决定以后的选择/选择组合的决定(根据需要做一个或另一个,提供者)

@Telastyn非常正确,LINQ提供商,例如如果你看一些实现代码,
所有表达减少等 是非常不确定的(即从提供者到提供者),他们可能最终映射到例如SQL
虽然这应该在大多数情况下映射相同的内容。