我最近在整个应用程序中开始使用Lazy,我想知道在使用Lazy<T>
时是否有任何明显的消极方面需要考虑?
我正在尝试尽可能多地使用Lazy<T>
,主要是为了帮助减少已加载但非活动插件的内存占用。
答案 0 :(得分:18)
我会对我的评论进行一些扩展,其内容如下:
我刚刚开始使用Lazy,并发现它通常是指示性的 糟糕的设计;或者程序员的懒惰。还有,一个 缺点是你需要更加警惕 变量,并创建适当的闭包。
例如,我使用Lazy<T>
来创建用户可以在我的(无会话)MVC应用中看到的页面。它是一个指导向导,因此用户可能希望转到随机之前的步骤。进行握手时,会创建一个Lazy<Page>
个对象数组,如果用户指定为步骤,则会评估该确切页面。我发现它提供了良好的性能,但有些方面我不喜欢,例如我的许多foreach
构造现在看起来像这样:
foreach(var something in somethings){
var somethingClosure = something;
list.Add(new Lazy<Page>(() => new Page(somethingClosure));
}
即。你必须非常积极地处理关闭问题。否则,我不认为存储lambda并在需要时对其进行评估是如此糟糕。
另一方面,这可能表明程序员是Lazy<Programmer>
,在某种意义上你不想现在考虑你的程序,而是在需要时让正确的逻辑进行评估,就像使用在我的例子中 - 我没有构建那个数组,而只是弄清楚具体请求的页面是什么;但是我选择了懒惰,并且全力以赴。
修改强>
我觉得Lazy<T>
在处理并发时也有一些诡计。例如,某些场景有ThreadLocal<T>
,特定多线程场景有几个标志配置。您可以在msdn上阅读更多内容。
答案 1 :(得分:8)
在我看来,你应该总是有理由选择懒惰。根据使用情况,有几种替代方案,并且肯定存在这种结构合适的情况。但不要仅仅因为它很酷而使用它。
例如,我在其他一个答案中没有得到页面选择示例中的要点。使用Lazy列表来选择单个元素可以直接使用委托列表或字典来完成,而无需使用Lazy或使用简单的switch语句。
所以最明显的替代方案是
与此相反,Lazy通常适用于
答案 2 :(得分:7)
这里并不是一个消极的方面,而是一个懒惰的人的问题:)。
惰性初始化器就像静态初始化器。他们运行一次。如果抛出异常,则会缓存异常,并且后续对.Value的调用将引发相同的异常。这是设计使然,在文档中提到... http://msdn.microsoft.com/en-us/library/dd642329.aspx:
valueFactory抛出的异常会被缓存。
因此,以下代码永远不会返回值:
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
{
}
}
答案 3 :(得分:5)
我开始使用Lazy<T>
主要是因为它在从数据库加载资源时具有并发功能。因此,我摆脱了锁定对象和可论证的锁定模式。
在我的情况下ConcurrentDictionary
+ Lazy
作为我的一天的价值,感谢@Reed Copsey和他的blog post
如下所示。而不是打电话:
MyValue value = dictionary.GetOrAdd( key, () => new MyValue(key));
我们会使用ConcurrentDictionary&gt;,和 写:
MyValue value = dictionary.GetOrAdd( key, () => new Lazy<MyValue>( () => new MyValue(key))) .Value;
到目前为止,Lazy<T>
没有发现任何缺点。
答案 4 :(得分:4)
与任何事情一样,Lazy<T>
可以用于善或恶,因此是一个缺点:如果使用不当,可能会导致混淆和沮丧。但是,懒惰的初始化模式已经存在多年了,现在.NET BCL有一个实现开发人员不需要再次重新发明轮子。更重要的是,MEF loves Lazy。
答案 5 :(得分:2)
“贯穿整个应用程序”
是什么意思?我认为只有在您不确定是否使用该值时才应该使用它,这可能只是需要很长时间才能计算的可选参数的情况。这可能包括复杂的计算,文件处理,Web服务,数据库访问等。
另一方面,为什么在这里使用Lazy
?在大多数情况下,您可以简单地调用方法而不是lazy.Value
,但无论如何它都没有区别。但是对于程序员来说,在没有Lazy
的情况下发生的事情更简单明了。
一个明显的好处可能已经实现了缓存价值,但我不认为这是一个很大的优势。
答案 6 :(得分:2)
Lazy用于保存资源,而不是真正需要的资源。这种模式非常好,但实现可能毫无用处。
资源越大,这种模式就越有用。
使用Lazy类的不足之处在于使用不透明。实际上,你必须在任何地方维护一个额外的间接(.Value)。 当你只需要一个真实类型的实例时,即使你不需要直接使用它也会被强制加载。
懒惰是为了提高生产力,但这种增益可能因高使用率而失去。
如果你有一个真正透明的实现(使用代理模式作为例子)它可以摆脱不利,它在很多情况下都非常有用。
并发性必须在另一个方面考虑,而不是默认情况下在您的类型中实现。它必须仅包含在客户端代码中或用于此概念的类型帮助程序。