C# 懒惰问题

发布于 2024-10-31 22:17:50 字数 346 浏览 1 评论 0原文

设计应用程序的常用方法是什么,这些方法强烈依赖于 C# 中的惰性求值(LINQIEnumerableIQueryable ,...)?

现在,我通常尝试使用 yield returnLINQ 查询使每个查询尽可能惰性,但在运行时这通常会导致“太懒” 行为,当每个查询从一开始就构建时,显然会导致严重的视觉性能下降。

我通常所做的是将 ToList() 投影运算符放在某处来缓存数据,但我怀疑这种方法可能不正确。

从一开始就设计此类应用程序的适当/常见方法是什么?

What's the common approach to design applications, which strongly rely on lazy evaluation in C# (LINQ, IEnumerable, IQueryable, ...)?

Right now I usually attempt to make every query as lazy as possible, using yield return and LINQ queries, but in runtime this could usually lead to "too lazy" behavior, when every query gets builts from it's beginning obviously resulting in severe visual performance degradation.

What I usually do means putting ToList() projection operators somewhere to cache the data, but I suspect this approach might be incorrect.

What's the appropriate / common ways to design this sort of applications from the very beginning?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

泪痕残 2024-11-07 22:17:50

我发现将每个 IEnumerable 分为三个类别之一很有用。

  1. 快的 - 例如列表和数组
  2. 慢的 - 例如数据库查询或繁重计算
  3. 非确定性的 - 例如 list.Select(x => new { ... })

对于类别 1,我倾向于在适当的时候保留具体类型,数组或 IList 等
对于类别 3,这些最好保留在方法内,以避免难以发现的错误。
然后我们有类别 2,和往常一样,在优化性能时,首先进行测量以找到瓶颈。

I find it useful to classify each IEnumerable into one of three categories.

  1. fast ones - e.g. lists and arrays
  2. slow ones - e.g. database queries or heavy calculations
  3. non-deterministic ones - e.g. list.Select(x => new { ... })

For category 1, I tend keep the concrete type when appropriate, arrays or IList etc.
For category 3, those are best to keep local within a method, to avoid hard-to find bugs.
Then we have category 2, and as always when optimizing performance, measure first to find the bottlenecks.

椵侞 2024-11-07 22:17:50

一些随机的想法 - 因为问题本身的定义很宽松:

  • 只有当结果可能不被使用时,惰性才是好的,因此只有在需要时才加载。然而,大多数操作都需要加载数据,因此惰性在这方面并不好。
  • 懒惰会导致困难的错误。我们已经在 ORM 中的数据上下文中看到了这一切
  • 对于 MEF 来说,惰性是好的

A few random thoughts - as the question itself is loosely defined:

  • Lazy is good only when the result might not be used hence loaded only when needed. Most operations, however, would need the data to be loaded so laziness is not good in that term.
  • Laziness can cause difficult bugs. We have seen it all with data contexts in ORMs
  • Lazy is good when it comes to MEF
深海少女心 2024-11-07 22:17:50

这是一个相当广泛的问题,不幸的是你会经常听到这个问题:这取决于情况。延迟加载是很棒的,直到它不再适用为止。

一般来说,如果您一遍又一遍地使用相同的 IEnumerables,最好将它们缓存为列表。

但无论怎样,让你的呼叫者知道这一点都没有什么意义。也就是说,如果您从存储库或其他东西获取 IEnumerables,最好让存储库完成其工作。它可能会在内部将其缓存为列表,或者可能每次都会构建它。如果你的调用者试图变得太聪明,他们可能会错过数据的变化等。

Pretty broad question and unfortunately you're going to hear this a lot: It depends. Lazy-loading is great until it's not.

In general, if you're using the same IEnumerables over and over it might be best to cache them as lists.

But rarely does it make sense for your callers to know this either way. That is, if you're getting IEnumerables from a repository or something, it is best to let the repository do its job. It might cache it as a list internally or it might build it up every time. If your callers try to get too clever they might miss changes in the data, etc.

苍风燃霜 2024-11-07 22:17:50

我建议在返回 DTO 之前在 DAL 中执行 ToList

public IList<UserDTO> GetUsers()
{
  using (var db = new DbContext())
  {
    return (from u in db.tblUsers
           select new UserDTO()
           {
               Name = u.Name
           }).ToList();
  }
}

在上面的示例中,您必须在 DbContext 作用域结束之前执行 ToList() 。

I would suggest doing a ToList in your DAL before returning the DTO

public IList<UserDTO> GetUsers()
{
  using (var db = new DbContext())
  {
    return (from u in db.tblUsers
           select new UserDTO()
           {
               Name = u.Name
           }).ToList();
  }
}

In the example above you have to do a ToList() before the DbContext scope ends.

稀香 2024-11-07 22:17:50

如果您需要缓存某个数据序列,请对该序列调用聚合运算符之一(ToListToArray 等)。否则就使用惰性评估。

围绕您的数据构建代码。哪些数据是不稳定的并且需要每次都更新?使用惰性求值并且不缓存。哪些数据是相对静态的,只需要拉取一次?将该数据缓存在内存中,这样您就不会不必要地提取它。

I you need a certain sequence of data to be cached, call one of the aggregation operators (ToList, ToArray, etc.) on that sequence. Otherwise just use lazy evaluation.

Build your code around your data. What data is volatile and needs to be pulled fresh each time? Use lazy evaluation and don't cache. What data is relatively static and only needs to be pulled once? Cache that data in memory so you don't pull it unnecessarily.

时光清浅 2024-11-07 22:17:50

延迟执行和使用 .ToList() 缓存所有项目并不是唯一的选择。第三个选项是在使用惰性列表进行迭代时缓存项目。

执行仍被推迟,但所有项目仅生成一次。其工作原理的示例:

public class LazyListTest
{
    private int _count = 0;

    public void Test()
    {
        var numbers = Enumerable.Range(1, 40);
        var numbersQuery = numbers.Select(GetElement).ToLazyList(); // Cache lazy
        var total = numbersQuery.Take(3)
            .Concat(numbersQuery.Take(10))
            .Concat(numbersQuery.Take(3))
            .Sum();
        Console.WriteLine(_count);
    }

    private int GetElement(int value)
    {
        _count++;
        // Some slow stuff here...
        return value * 100;
    }
}

如果运行 Test() 方法,则 _count 只有 10。如果没有缓存,则为 16,如果使用 .ToList(),则为 16。 40岁了!

LazyList 的实现可以在这里找到

Deferred execution and caching all items with .ToList() are not the only options. The third option is to cache the items while you are iterating by using a lazy List.

The execution is still deferred but all items are only yielded once. An example of how this work:

public class LazyListTest
{
    private int _count = 0;

    public void Test()
    {
        var numbers = Enumerable.Range(1, 40);
        var numbersQuery = numbers.Select(GetElement).ToLazyList(); // Cache lazy
        var total = numbersQuery.Take(3)
            .Concat(numbersQuery.Take(10))
            .Concat(numbersQuery.Take(3))
            .Sum();
        Console.WriteLine(_count);
    }

    private int GetElement(int value)
    {
        _count++;
        // Some slow stuff here...
        return value * 100;
    }
}

If you run the Test() method, the _count is only 10. Without caching it would be 16 and with .ToList() it would be 40!

An example of the implementation of LazyList can be found here.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文