有什么理由不使用null-coalescing运算符进行延迟初始化?

Rod*_*ley 13 c# null-coalescing-operator lazy-initialization

问候我今天正在做一些懒惰的初始化代码,并且想到为什么不使用null-coalescing运算符来执行此操作,它更短,但后来我认为这样做有任何开销或额外成本.

下面是简化的示例代码,显示了一个用于延迟初始化的更常见的表单,然后是一个使用null-coalescing运算符的表单.它们具有完全相同的结果,并且看起来相同.我的第一个想法是,在创建对象之后,现在可以使用它自己进行额外的赋值??.这是一个非问题,编译器/ JIT优化了一些如何,是否有一些更邪恶的事情,你应该永远不要做懒惰的初始化??,或者它是完全安全的,没有坏mojo可以来自它.

private MyLazyObject _lazyObject;

public MyLazyObject GetMyLazyObjectUsingMoreCommonMethod()
{
    if (_lazyObject != null)
        return _lazyObject;

    _lazyObject = new MyLazyObject();

    return _lazyObject;
}

public MyLazyObject GetMyLazyObjectUsingNullCoalescingOpMethod()
{
    _lazyObject = _lazyObject ?? new MyLazyObject();
    return _lazyObject;
}
Run Code Online (Sandbox Code Playgroud)

Str*_*ior 14

是的,一个叫做线程安全的小东西.你给出的两个方法在功能上是等价的,所以null合并运算符本身并不坏,但是你列出的方法都不是线程安全的,所以如果两个线程试图同时调用你的Get方法,你最终会产生两个MyLazyObjects.这可能不是什么大问题,但它可能不是你所希望的.

如果您使用的是.NET 4,请使用Lazy.

private Lazy<MyLazyObject> _lazyObject = 
    new Lazy<MyLazyObject>(() => new MyLazyObject());

public MyLazyObject MyLazyObject {get {return _lazyObject.Value;}}
Run Code Online (Sandbox Code Playgroud)

代码简洁,易于理解,并且线程安全.

  • 我在这里有点不同意; 线程安全是一个非常特殊的主题,**绝大多数类的类不需要是线程安全的(事实上:如果它们是,它比每个成员更微妙).除非明确地声明线程安全*作为一项要求,否则引入它是非常人为的(除非专门设计,否则该类不太可能作为线程安全单元工作).`Lazy <T>`增加了一点开销,大部分时间都是不必要的. (4认同)
  • @Rodney如果线程*是一个问题,那么这确实是一个非常恰当的答案 (2认同)
  • @Marc Gravell:+1 很好的论证。根据类的使用目的,“Lazy”可能不是最好的解决方案。根据我的个人经验,延迟实例化的对象通常旨在供多个线程使用,但并非所有人都如此。只是当有人问“这绝对安全吗?” 我认为即使不是明确要求,也值得介绍这种可能性。 (2认同)
  • 此外,您还可以使用 LazyThreadSafetyMode.None 禁用 Lazy&lt;T&gt; 中的线程安全开销,并且仍然具有易于理解意图的代码,并且可以在以后需要时启用线程安全。 (2认同)

Mar*_*ell 5

它非常安全且定义良好 - 事实上,它意味着编译器可以只复制堆栈的头部(dup)并存储一次,而不是存储字段,load-field.

它唯一出现问题的是c#1.2(.NET 1.1),它不存在.