2011-04-12 148 views
5

什么是设计应用程序的常用方法,它强烈依赖于C#LINQIEnumerable,IQueryable,...)中的惰性评估?C#懒惰问题

现在我通常试图使每个查询懒越好,使用yield returnLINQ的查询,但在运行时,这可能通常会导致“懒得”行为,当每一个查询获取从中竣工图就要开始明显导致在严重的视觉表现下降。

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

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

回答

4

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

  1. 快速的 - 例如,列表和阵列
  2. 慢的 - 例如数据库查询或重计算
  3. 非确定性的 - 例如, list.Select(X =>新{...})

对于1类,我倾向于保持混凝土类型在适当的时候,阵列或IList的等 对于3类,这些都是最好的本地内保持一种方法,以避免难以发现的错误。 然后我们有第2类,并且一如既往在优化性能时,首先要测量以找出瓶颈。

2

一些随机的想法 - 因为这个问题本身是松散的定义:

  • 懒惰是好的,只有当结果可能不被使用,因此只在需要时加载。但是,大多数操作都需要加载数据,因此在这个术语中惰性并不好。
  • 懒惰会造成难题。我们已经看到了这一切,在奥姆斯数据上下文
  • 懒惰是好的,当谈到MEF
1

我需要的数据有一定的顺序进行缓存,调用聚合运营商之一(ToListToArray,等等)。否则,只需使用懒惰评估。

围绕您的数据构建代码。什么数据是不稳定的,每次都需要重新刷新?使用懒惰评估,不要缓存。哪些数据相对静态,只需要拉一次?将这些数据缓存在内存中,以免不必要地使用它。

2

非常广泛的问题,不幸的是你会听到很多:这取决于。延迟加载很好,直到没有。

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

但是,对于您的呼叫者来说,这种情况很少有人知道这一点。也就是说,如果您从存储库或某个东西获取IEnumerables,最好让存储库完成其工作。它可能会将其缓存为内部列表,也可能每次都将其构建起来。如果来电者试图让太聪明了,他们可能会错过数据改变等

2

我会建议返回DTO

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

在你上面的例子做之前你DAL一个ToList 在DbContext作用域结束之前执行ToList()。

+1

神圣莫里!我有一吨代码,看起来几乎与此相同! :) – 2011-04-12 12:41:48

0

推迟执行并使用.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; 
    } 
} 

如果您运行测试()方法时,_count只有10.没有缓存这将是16,并用。 ToList()这将是40!

implementation of LazyList can be found here的示例。