任何不使用空合并运算符进行延迟初始化的好理由

本文关键字:初始化 延迟 理由 合并 运算符 任何不 | 更新日期: 2023-09-27 18:08:25

你好,我今天正在做一些懒惰的初始化代码,并认为为什么不使用空合并运算符来做这件事,它更短,但后来我认为这样做有任何开销或额外的成本。

下面是简化的示例代码,显示了延迟初始化的更常见形式,然后是使用空合并操作符的形式。它们有完全相同的结果,并且看起来是相等的。我的第一个想法是,在对象被创建之后,现在有一个额外的分配给它自己使用??。这是一个没有问题的编译器/JIT以某种方式优化这个,是否有更邪恶的事情发生,你不应该对??进行延迟初始化,或者它是完全安全的,没有坏的魔咒可以来自它。

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;
}

任何不使用空合并运算符进行延迟初始化的好理由

是的,一个叫做线程安全的小东西。您给出的两个方法在功能上是等效的,因此空合并运算符本身并不坏,但是您列出的两种方法都不是线程安全的,因此,如果两个线程试图同时调用您的Get方法,您可能最终产生两个MyLazyObject。这可能不是什么大事,但它可能不是你所希望的。

如果你使用的是。net 4,就使用Lazy

private Lazy<MyLazyObject> _lazyObject = 
    new Lazy<MyLazyObject>(() => new MyLazyObject());
public MyLazyObject MyLazyObject {get {return _lazyObject.Value;}}

代码简洁,易于理解,线程安全。

这是完全安全且定义良好的-实际上,这意味着编译器可以复制堆栈头(dup)并存储一次,而不是存储字段,加载字段。

唯一出现问题的地方是c# 1.2。

语法糖中的空合并操作符。从本质上讲,它与您的第一个示例相同,我不认为JIT编译器对它进行了特殊的优化。您应该更关心的是方法的线程安全性。null合并操作符不是原子的,这意味着您应该确保在返回之前以线程安全的方式实例化MyLazyObject

您甚至可以更改第一个方法的代码:

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

这将提供与

相同的IL
public MyLazyObject GetMyLazyObjectUsingNullCoalescingOpMethod()
{
    _lazyObject = _lazyObject ?? new MyLazyObject();
    return _lazyObject;
}

如前所述,这只是语法糖。