はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【C#】System.Lazy<T>を用いてスレッドセーフな遅延初期化を行う

はじめに

今回はSystem.Lazy<T>について取り上げたいと思います。

learn.microsoft.com

概要

System.Lazy<T>を使うことで、スレッドセーフな遅延初期化をすることができます。

遅延初期化のサポートを提供します。

遅延初期化を使用して、大規模なオブジェクトまたはリソースを集中的に使用するオブジェクトの作成、またはリソースを集中的に使用するタスクの実行を延期します。特に、プログラムの有効期間中にこのような作成または実行が発生しない可能性がある場合。

https://learn.microsoft.com/ja-jp/dotnet/api/system.lazy-1?view=net-7.0

そもそも遅延初期化ってなんやねんって話ですが、ざっくり言うとオブジェクトを利用するまで生成しないでおくということです。

// Lazyを使わずに遅延初期化 (スレッドセーフでない)
public class Sample
{
    private long[] _data;
            
    // C#8.0から「??=」(Null合体割り当て演算子)を利用可
    public long[] Data => _data ??= new long[1000];
}

使い方

// メモリバカ喰いクラス
public class LargeObject
{
    public readonly long[] Data = new long[100000];
}
        
public static void Main(string[] args)
{
    // Lazyを利用することで、オブジェクトの生成を遅延することができる
    // この時点でLargeObjectのオブジェクトはまだ生成されていない
    var lazyLargeObject = new Lazy<LargeObject>(() => new LargeObject());
            
    // Lazy<T>.IsValueCreatedで、LargeObjectのオブジェクトが生成されているか調べることができる
    // false
    Console.WriteLine(lazyLargeObject.IsValueCreated);

    // Lazy<T>.Valueにアクセスすることで、LargeObjectのオブジェクトが生成される
    var x = lazyLargeObject.Value.Data[0];
            
    // true
    Console.WriteLine(lazyLargeObject.IsValueCreated);
            
            
    // スレッドセーフにはコストがかかる
    // 遅延初期化のみ利用したい場合はisThreadSafeをfalseにしてあげる
    var lazyLargeObject2 = new Lazy<LargeObject>(() => new LargeObject(), isThreadSafe: false);
}