问候我今天正在做一些懒惰的初始化代码,并且想到为什么不使用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;
}
答案 0 :(得分:14)
是的,一个叫做线程安全的小东西。你给出的两个方法在功能上是等价的,所以空合并运算符本身并不坏,但是你列出的方法都不是线程安全的,所以如果两个线程试图调用你的Get
方法与此同时,你最终可能会产生两个MyLazyObject
。这可能不是什么大问题,但它可能不是你所希望的。
如果您使用的是.NET 4,请使用Lazy
。
private Lazy<MyLazyObject> _lazyObject =
new Lazy<MyLazyObject>(() => new MyLazyObject());
public MyLazyObject MyLazyObject {get {return _lazyObject.Value;}}
代码简洁,易于理解,并且线程安全。
答案 1 :(得分:5)
它非常安全且定义良好 - 事实上,它意味着编译器只需复制堆栈的头部(dup)并存储一次,而不是存储字段,加载字段。
唯一出现问题的是c#1.2(.NET 1.1),它不存在。
答案 2 :(得分:2)
语法糖中的空合并运算符。本质上它与您的第一个示例相同,我不相信JIT编译器对它进行了特殊的优化。你应该更关心的是你的方法的线程安全性。 null合并运算符不是原子的,这意味着您应该确保在返回之前以线程安全的方式实例化MyLazyObject
。
答案 3 :(得分:2)
您甚至可以使用第一种方法的代码:
public MyLazyObject GetMyLazyObjectUsingMoreCommonMethod()
{
if (_lazyObject == null)
_lazyObject = new MyLazyObject();
return _lazyObject;
}
这将提供与
相同的ILpublic MyLazyObject GetMyLazyObjectUsingNullCoalescingOpMethod()
{
_lazyObject = _lazyObject ?? new MyLazyObject();
return _lazyObject;
}
如上所述,这只是语法糖。