C# 懒惰的缺点<;T>;?

C# 懒惰的缺点<;T>;?,c#,mef,C#,Mef,我最近开始在整个应用程序中使用,我想知道在使用Lazy时是否有任何明显的负面因素需要考虑 我正试图尽可能多地利用惰性,主要是为了帮助减少已加载但不活跃的插件的内存占用。与任何事情一样,惰性可以用于善或恶,因此有一个缺点:如果使用不当,可能会导致混乱和挫折。然而,惰性初始化模式已经存在多年了,现在.NETBCL已经有了一个实现,开发人员不再需要重新发明轮子。更重要的是,.我将对我的评论进行一些扩展,内容如下: 我刚开始使用Lazy,发现它通常是指示性的 设计不好;或者程序员的懒惰。还有一个 缺点是

我最近开始在整个应用程序中使用,我想知道在使用
Lazy
时是否有任何明显的负面因素需要考虑


我正试图尽可能多地利用
惰性
,主要是为了帮助减少已加载但不活跃的插件的内存占用。

与任何事情一样,
惰性
可以用于善或恶,因此有一个缺点:如果使用不当,可能会导致混乱和挫折。然而,惰性初始化模式已经存在多年了,现在.NETBCL已经有了一个实现,开发人员不再需要重新发明轮子。更重要的是,.

我将对我的评论进行一些扩展,内容如下:

我刚开始使用Lazy,发现它通常是指示性的 设计不好;或者程序员的懒惰。还有一个 缺点是,你必须更加警惕的范围了 变量,并创建适当的闭包

例如,我使用了
Lazy
来创建用户可以在我的(无会话)MVC应用程序中看到的页面。这是一个向导,因此用户可能希望转到随机的上一步。进行握手时,将装箱一组
惰性
对象,如果用户指定为步骤,则会计算准确的页面。我发现它提供了良好的性能,但有一些方面我不喜欢,例如我的许多
foreach
结构现在看起来如下:

foreach(var something in somethings){
     var somethingClosure = something;
     list.Add(new Lazy<Page>(() => new Page(somethingClosure));
} 
foreach(在something中变量something){
var somethingClosure=某物;
添加(newlazy(()=>newpage(somethingClosure));
} 
也就是说,您必须非常主动地处理闭包问题。否则,我认为存储lambda并在需要时对其进行评估不会对性能造成太大影响

另一方面,这可能表明程序员是一个
懒惰的
,从某种意义上说,您不希望现在就仔细思考您的程序,而是在需要时让适当的逻辑进行评估,就像我的例子一样-我不需要构建该数组,而只需要找出特定请求的页面将是什么是的,但我选择了懒惰,采取了一种全方位的方法

编辑


我突然想到,
Lazy
在处理并发时也有一些特殊之处。例如,对于某些场景,有一个
ThreadLocal
,对于特定的多线程场景,有几个标志配置。您可以阅读更多内容。

您对“在我的应用程序中”的确切含义是什么

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

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


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

这不是一个很消极的方面,但对懒惰的人来说是一个难题:)

惰性初始值设定项类似于静态初始值设定项。他们跑了一次。如果引发异常,则会缓存该异常,对.Value的后续调用将引发相同的异常。这是经过设计的,并在文件中提到…:

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
    {

    }
}
bool firstTime=true;
懒惰懒汉=新懒汉(()=>
{
如果(第一次)
{
第一次=错误;
抛出新异常(“总是在第一次抛出异常。”);
}
返回21;
});
智力?val=null;
while(val==null)
{
尝试
{
val=懒散值;
}
抓住
{
}
}

在我看来,你应该总是有理由选择懒惰。根据用例的不同,有几种替代方案,并且这种结构肯定是合适的。但不要仅仅因为它很酷就使用它

例如,在其他答案中,我没有得到页面选择示例中的要点。使用Lazy列表选择单个元素可以直接使用委托列表或字典,而无需使用Lazy或简单的switch语句

因此,最明显的选择是

  • 直接实例化廉价的数据结构或无论如何都需要的结构
  • 在某些算法中需要零到几次的委托
  • 某些缓存结构用于在一段时间内不使用时应释放内存的项
  • 某种“未来”结构,如任务,在实际使用之前可能已经开始异步初始化,在以后需要该结构的可能性很高的情况下,会消耗空闲CPU时间
与此相反,懒惰通常适用于以下情况:

  • 计算密集型数据结构
  • 在某些算法中,如果零概率很大,则需要零到多次
  • 数据是某个方法或类的本地数据,在不再使用时可以进行垃圾收集,或者应该在整个程序运行时将数据保存在内存中

我开始使用
Lazy
,主要是因为它在从数据库加载资源时具有并发功能。因此,我摆脱了锁定对象和有争议的锁定模式。 就我而言,
ConcurrentDictionary
+
Lazy
,感谢@Reed Copsey和他的团队,这是我一天中的价值所在

如下所示。而不是打电话:

MyValue value = dictionary.GetOrAdd(
                             key, 
                             () => new MyValue(key));
我们将使用ConcurrentDi
MyValue value = dictionary.GetOrAdd(
                             key, 
                             () => new Lazy<MyValue>(
                                 () => new MyValue(key)))
                          .Value;