懒惰的缺点<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助手中。