我最近开始在我的应用程序中广泛使用Lazy,我想知道在使用Lazy<T>
时是否有任何明显的负面影响需要考虑?
我试图尽可能多地利用Lazy<T>
,主要是为了帮助减少我们已加载但未激活插件的内存占用。
我最近开始在我的应用程序中广泛使用Lazy,我想知道在使用Lazy<T>
时是否有任何明显的负面影响需要考虑?
我试图尽可能多地利用Lazy<T>
,主要是为了帮助减少我们已加载但未激活插件的内存占用。
我想进一步解释我的评论,它写道:
我刚开始使用 Lazy,发现它经常表明设计存在问题;或者是程序员的懒惰。另外,一个缺点是你必须更加警惕作用域变量,并创建适当的闭包。
例如,我使用 Lazy<T>
来创建我的(无会话)MVC应用中用户可以看到的页面。这是一个引导式向导,因此用户可能要前往随机的上一个步骤。当握手完成后,创建了一个Lazy<Page>
对象数组,如果用户指定了一个步骤,那么就会评估该精确页面。 我发现它提供了良好的性能,但其中有一些方面我不喜欢,例如许多我的foreach
结构现在看起来像这样:
foreach(var something in somethings){
var somethingClosure = something;
list.Add(new Lazy<Page>(() => new Page(somethingClosure));
}
也就是说,你必须非常主动地处理闭包的问题。否则,我认为将一个lambda表达式存储起来,在需要时再进行评估并不会带来太大的性能损失。
另一方面,这也可能表明程序员正在成为一个“懒惰的Programmer
”,也就是说,你更愿意不去思考程序,而是让正确的逻辑在需要时进行评估,就像在我的例子中一样——我可以只找出所需页面的具体内容,而不是构建整个数组;但我选择了懒惰的方式,采取了全盘接受的方法。
编辑
我意识到,在处理并发时,Lazy<T>
也有一些奇特之处。例如,在某些情况下可以使用ThreadLocal<T>
,还有一些标志配置可供您的特定多线程场景使用。你可以在MSDN上阅读更多相关信息。
Lazy<T>
本身的问题,而是你使用它的方式。 - Anton Gogolevvar somethingClosure = something;
我一直在寻找一个称呼我的闭包... 哦,我真是后悔莫及。 - satnhak在我看来,你应该总是有选择使用 Lazy 的理由。根据使用情况,有几种替代方案,并且肯定存在适用于此结构的情况。但不要仅仅因为它很酷而使用它。
例如,在其他答案中的页面选择示例中,我不明白其中的重点。使用 Lazy 列表选择单个元素可以直接使用委托列表或字典而不使用 Lazy 或简单的 switch 语句来完成。
因此,最明显的替代方案是
与此相反,Lazy 经常适用于以下情况:
因此,下面的代码永远不会返回值: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
{
}
}
LazyWithNoExceptionCaching
- https://dev59.com/klsW5IYBdhLWcg3w2aKK#42567351 - mjwillsLazy<T>
是因为它在从数据库加载资源时具有并发能力。因此,我摆脱了锁对象和可能产生歧义的锁模式。在我的情况下,ConcurrentDictionary
+ Lazy
作为值使我的工作更轻松愉快,感谢@Reed Copsey和他的博客帖子。
This looks like the following. Instead of calling:
MyValue value = dictionary.GetOrAdd( key, () => new MyValue(key));
We would instead use a ConcurrentDictionary>, and write:
MyValue value = dictionary.GetOrAdd( key, () => new Lazy<MyValue>( () => new MyValue(key))) .Value;
目前还没有发现Lazy<T>
的任何缺点。
Lazy<T>
可以用于好事或坏事,因此有一个缺点:如果不适当地使用它,可能会导致混淆和挫败感。然而,惰性初始化模式已经存在多年,现在 .NET BCL 有了一个实现,开发人员不需要再次重新发明轮子。更重要的是,MEF 喜欢 Lazy。Lazy用于在不需要时保留资源。这个模式相当好,但实现可能没有用。
资源越大,此模式越有用。
使用Lazy类的一个缺点是使用的不透明性。确实,您必须在每个地方维护一个额外的间接引用(.Value)。即使您不需要直接使用它,当您只需要真实类型的实例时,它也被强制加载。
Lazy是用于懒惰开发以提高生产率的,但是过度使用可能会失去这种收益。
如果您有一个真正透明的实现(例如使用代理模式),则可以摆脱缺点,并且在许多情况下非常有用。
并发必须考虑另一方面,而不是默认在您的类型中实现。它必须仅包括在客户端代码或类型助手中以供此概念使用。
你在说“Throughout my application”指的是什么?
我认为只有当你不确定值是否会被使用时,才应该使用它。这可能只适用于需要长时间计算的可选参数。这可能包括复杂的计算、文件处理、Web服务、数据库访问等等。
另一方面,为什么要在这里使用Lazy
呢?在大多数情况下,你可以直接调用一个方法而不是lazy.Value
,而且这样做也没有任何区别。但是,对于程序员来说,在没有Lazy
的情况下更简单和明显。
一个明显的好处可能是已经实现了值的缓存,但我认为这并不是一个很大的优势。