懒惰的缺点<T>?

我最近开始在我的应用程序中使用Lazy ,而且我想知道在使用Lazy<T>时是否需要考虑一些明显的负面因素?

我试图尽可能多地使用Lazy<T> ,主要是为了减less我们加载的但是不活动的插件的内存占用。

我会扩大一点我的评论,其内容如下:

我刚开始使用懒惰,并发现它往往是坏的devise的指示; 或程序员的懒惰。 此外,一个缺点是你必须更加警惕的范围variables,并创build适当的closures。

例如,我使用Lazy<T>来创build用户可以在我的(无会话 )MVC应用程序中看到的页面。 这是一个引导向导,所以用户可能想要去一个随机的一步。 进行握手时,将创build一个Lazy<Page>对象数组,如果用户指定为步骤,则会评估该确切页面。 我发现它提供了良好的性能,但有一些方面我不喜欢,例如我的许多foreach结构现在看起来像这样:

 foreach(var something in somethings){ var somethingClosure = something; list.Add(new Lazy<Page>(() => new Page(somethingClosure)); } 

即你必须非常积极地处理closures问题。 否则,我不认为这是一个糟糕的性能打存储lambda和评估需要时。

另一方面,这可能表明程序员是一个Lazy<Programmer> ,因为你现在不想通过你的程序来思考,而是在需要的时候让适当的逻辑来评估,就像我的例子一样 – 而不是build立这个数组,我只能弄清楚那个特定的请求页面是什么; 但我select了懒惰,并且采取一种全面的方法。

编辑

我发现Lazy<T>在使用并发时也有一些问题。 例如,对于某些场景,有一个ThreadLocal<T> ,以及针对您的特定multithreading场景的多个标志configuration。 你可以阅读更多的MSDN 。

这不是一个消极的方面,但懒惰的人:)。

延迟初始化器就像静态初始化器。 他们得到运行一次 。 如果引发exception,则caching该exception,随后对.Value的调用将抛出相同的exception。 这是由devise,并在文档中提到… http://msdn.microsoft.com/en-us/library/dd642329.aspx

被valueFactory引发的exception被caching。

因此,下面的代码永远不会返回一个值:

 bool firstTime = true; Lazy<int> lazyInt = new Lazy<int>(() => { if (firstTime) { firstTime = false; throw new Exception("Always throws exception the very first time."); } return 21; }); int? val = null; while (val == null) { try { val = lazyInt.Value; } catch { } } 

在我看来,你应该总是有一个select懒惰的理由。 根据用例有几种select,肯定有这种结构适合的情况。 但不要仅仅因为它很酷就使用它。

例如,我不明白其他答案之一中的页面select示例。 使用Lazy列表select单个元素可以直接使用委托的列表或字典,而无需使用Lazy或简单的switch语句。

所以最明显的select是

  • 直接实例化便宜的数据结构或结构
  • 在一些algorithm中代表零到几次需要的东西
  • 某些项目的caching结构,当不使用一段时间时应该释放内存
  • 某种“未来”的结构像Task,在实际使用之前可能会asynchronous地开始初始化,在耗费空闲CPU时间的情况下,如果后面的结构需要相当高的概率

相比之下,懒惰往往是适合的时候

  • 计算密集的数据结构
  • 在零情况具有显着概率的一些algorithm中需要零次或多次
  • 并且数据对某些方法或类是本地的,并且可以在不再使用时被垃圾回收,或者数据应该被保存在整个程序运行时的内存中

我使用Lazy<T>主要是因为它在从数据库加载资源时是并发能力。 因此,我摆脱了locking对象和可争论的locking模式。 在我的案例中, ConcurrentDictionary + Lazy作为一个值得我的一天,感谢@Reed Copsey和他的博客文章

这看起来像下面这样。 不要打电话:

 MyValue value = dictionary.GetOrAdd( key, () => new MyValue(key)); 

我们将使用一个ConcurrentDictionary>,并写:

 MyValue value = dictionary.GetOrAdd( key, () => new Lazy<MyValue>( () => new MyValue(key))) .Value; 

Lazy<T>目前还没有被发现。

与其他任何东西一样, Lazy<T>可以用于善恶,因此是一个缺点:如果使用不当,可能会造成困惑和挫折。 然而,懒惰的初始化模式已经存在多年了,现在.NET BCL已经有了一个实现,开发人员不需要再次重新发明轮子。 更重要的是, MEF喜欢懒惰 。

你的意思是什么“整个我的申请”

我认为只有当你不确定值是否被使用时才应该使用它,这可能只是需要很长时间计算的可选参数。 这可能包括复杂的计算,文件处理,Web服务,数据库访问等等。

另一方面,为什么在这里使用Lazy ? 在大多数情况下,你可以简单地调用一个方法,而不是lazy.Value ,它没有任何区别。 但是对于程序员来说,更简单明显的是没有Lazy情况。

一个明显的好处可能已经实现了caching的价值,但我不认为这是一个很大的优势。

懒惰是用来保存资源,而不是真的需要。 这种模式是相当不错的,但实现可能是无用的。

资源越大,有用的就是这种模式。

使用Lazy类的缺点是使用的不透明性。 事实上,你必须在任何地方维护一个额外的间接(.Value)。 当你只需要一个真实types的实例时,即使你不需要直接使用它,它也会被强制加载。

懒惰是懒惰的发展获得生产力,但这种收益可以通过高使用率损失。

如果你有一个真正的透明的实现(例如使用代理模式),它消除了不利因素,在许多情况下它可以是非常有用的。

并发性必须在其他方面考虑,而不是在你的types默认实现。 它只能被包含在客户端代码或types助手中。