15

あいさつ今日、怠惰な初期化コードを実行していて、null合体演算子を使用してこれを実行しないのはなぜかと思いました。短いですが、この方法で実行するためのオーバーヘッドや追加コストがあると思いました。

以下は、遅延初期化に使用されるより一般的な形式と、null合体演算子を使用する形式を示す簡略化されたサンプルコードです。それらはまったく同じ結果をもたらし、同等に見えます。私の最初の考えは、オブジェクトが作成された後、を使用してそれ自体に追加の割り当てがあるということ??です。これは問題ではなく、コンパイラ/ 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;
}
4

4 に答える 4

16

はい、スレッドセーフと呼ばれるちょっとしたことです。指定した2つのメソッドは機能的に同等であるため、null合体演算子自体は悪くありませんが、リストしたアプローチはどちらもスレッドセーフではないため、2つのスレッドGetが同時にメソッドを呼び出そうとすると、最終的に 2 つの を生成する可能性がありますMyLazyObject。それは大したことではないかもしれませんが、おそらくあなたが望んでいるものではないでしょう.

.NET 4 を使用している場合は、Lazy.

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

public MyLazyObject MyLazyObject {get {return _lazyObject.Value;}}

コードは簡潔で理解しやすく、スレッドセーフです。

于 2011-09-13T21:34:13.007 に答える
5

これは完全に安全で、明確に定義されています。実際、コンパイラは、ストア フィールド、ロード フィールドではなく、スタックの先頭をコピー (dup) して 1 回ストアできることを意味します。

それが問題になるのは、それが存在しない c# 1.2 (.NET 1.1) だけです。

于 2011-09-13T21:37:45.260 に答える
2

最初のメソッドのコードを変更することもできます。

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

    return _lazyObject;
}

これにより、以下と同じ IL が配信されます。

public MyLazyObject GetMyLazyObjectUsingNullCoalescingOpMethod()
{
    _lazyObject = _lazyObject ?? new MyLazyObject();

    return _lazyObject;
}

言ったように、それは単なる構文糖衣です。

于 2011-09-13T21:43:52.967 に答える
2

シンタックス シュガーの null 合体演算子。基本的には最初の例と同じであり、JIT コンパイラが特別な最適化を行っているとは思いません。もっと気をつけなければならないのは、メソッドのスレッドセーフです。null 合体演算子はアトミックではありません。つまり、MyLazyObject返す前にスレッドセーフな方法でインスタンス化する必要があります。

于 2011-09-13T21:36:12.933 に答える