繁体   English   中英

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

[英]What is the preferred (performant and readable) way of chaining IEnumerable<T> extension methods?

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

我对任何扩展方法和 lambda 用法持开放态度,但我不想使用 LINQ 语法以与代码库的 rest 保持一致。

将过滤推送到SelectMany()方法的selector还是仅链接另一个Where()方法更好? 或者有更好的解决方案吗?

我将如何确定最佳选择 go? 在这个测试用例中,一切都可以直接在 memory 中获得。显然,下面的两个示例目前都产生了相同的正确结果; 我只是在寻找一个或另一个(或另一个选项)会更受欢迎的原因。

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();
    }

这是示例 object 图:

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 实现而异。 LinqToSql 的行为与内存中过滤不同。 子句的顺序应该根据使用的数据影响性能,因为天真的实现将在序列中较早地过滤更多记录,这意味着后面方法的工作较少。

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

至于确定最佳选择,它与其他任何事情都一样:衡量。

我猜你的第一个表达式会稍微快一点但微不足道。 要真正确定哪个更快,您需要使用分析器或秒表为它们计时。

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

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

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

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”-s 等,对我来说也不是那么可读。 虽然使用“缩写形式”,但很清楚哪个是哪个,哪里,select 等,而且它非常“速记”,字符少得多。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM