Lazy<T> クラス

定義

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

generic <typename T>
public ref class Lazy
public class Lazy<T>
[System.Runtime.InteropServices.ComVisible(false)]
[System.Serializable]
public class Lazy<T>
type Lazy<'T> = class
[<System.Runtime.InteropServices.ComVisible(false)>]
[<System.Serializable>]
type Lazy<'T> = class
Public Class Lazy(Of T)

型パラメーター

T

遅延初期化されているオブジェクトの型。

継承
Lazy<T>
派生
属性

次の例では、クラスを Lazy<T> 使用して、複数のスレッドからのアクセスで遅延初期化を提供する方法を示します。

注意

この例では、コンストラクターを Lazy<T>(Func<T>) 使用します。 また、コンストラクター (指定対象) とLazy<T>(Func<T>, LazyThreadSafetyMode)コンストラクター (指定true対象isThreadSafe) の使用方法Lazy<T>(Func<T>, Boolean)LazyThreadSafetyMode.ExecutionAndPublicationmode示します。 別のコンストラクターに切り替えるには、コメントアウトするコンストラクターを変更するだけです。

同じコンストラクターを使用した例外キャッシュの例については、コンストラクターを Lazy<T>(Func<T>) 参照してください。

この例では、複数あるスレッドのうちの 1 つによって遅れて初期化される LargeObject クラスを定義します。 コードの 4 つの主要なセクションは、初期化子、ファクトリ メソッド、実際の初期化、およびオブジェクトの作成時にメッセージを表示するクラスの LargeObject コンストラクターの作成を示しています。 Main メソッドの開始時に、この例では LargeObject のスレッドセーフな遅延初期化子が作成されます。

lazyLargeObject = new Lazy<LargeObject>(InitLargeObject);

// The following lines show how to use other constructors to achieve exactly the
// same result as the previous line:
//lazyLargeObject = new Lazy<LargeObject>(InitLargeObject, true);
//lazyLargeObject = new Lazy<LargeObject>(InitLargeObject,
//                               LazyThreadSafetyMode.ExecutionAndPublication);
let lazyLargeObject = Lazy<LargeObject> initLargeObject

// The following lines show how to use other constructors to achieve exactly the
// same result as the previous line:
//     let lazyLargeObject = Lazy<LargeObject>(initLargeObject, true)
//     let lazyLargeObject = Lazy<LargeObject>(initLargeObject,
//                               LazyThreadSafetyMode.ExecutionAndPublication)
lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject)

' The following lines show how to use other constructors to achieve exactly the
' same result as the previous line: 
'lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject, True)
'lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject, _
'                               LazyThreadSafetyMode.ExecutionAndPublication)

ファクトリ メソッドは、オブジェクトの作成と、さらに初期化するためのプレースホルダーを示します。

static LargeObject InitLargeObject()
{
    LargeObject large = new LargeObject(Thread.CurrentThread.ManagedThreadId);
    // Perform additional initialization here.
    return large;
}
let initLargeObject () =
    let large = LargeObject Thread.CurrentThread.ManagedThreadId
    // Perform additional initialization here.
    large
Private Shared Function InitLargeObject() As LargeObject
    Dim large As New LargeObject(Thread.CurrentThread.ManagedThreadId)
    ' Perform additional initialization here.
    Return large
End Function

最初の 2 つのコード セクションは、次に示すようにラムダ関数を使用して結合できることに注意してください。

lazyLargeObject = new Lazy<LargeObject>(() =>
{
    LargeObject large = new LargeObject(Thread.CurrentThread.ManagedThreadId);
    // Perform additional initialization here.
    return large;
});
let lazyLargeObject = Lazy<LargeObject>(fun () ->
    let large = LargeObject Thread.CurrentThread.ManagedThreadId
    // Perform additional initialization here.
    large)
lazyLargeObject = New Lazy(Of LargeObject)(Function () 
    Dim large As New LargeObject(Thread.CurrentThread.ManagedThreadId) 
    ' Perform additional initialization here.
    Return large
End Function)

この例では、遅延初期化が発生する前に不確定期間が経過する可能性があることを示すために一時停止します。 Enter キーを押すと、3 つのスレッドが作成されて開始されます。 3 つのスレッドすべてで使用されるメソッドは ThreadProc 、プロパティを Value 呼び出します。 これが初めて発生すると、インスタンスが LargeObject 作成されます。

LargeObject large = lazyLargeObject.Value;

// IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
//            object after creation. You must lock the object before accessing it,
//            unless the type is thread safe. (LargeObject is not thread safe.)
lock(large)
{
    large.Data[0] = Thread.CurrentThread.ManagedThreadId;
    Console.WriteLine("Initialized by thread {0}; last used by thread {1}.",
        large.InitializedBy, large.Data[0]);
}
let large = lazyLargeObject.Value

// IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
//            object after creation. You must lock the object before accessing it,
//            unless the type is thread safe. (LargeObject is not thread safe.)
lock large (fun () ->
    large.Data[0] <- Thread.CurrentThread.ManagedThreadId
    printfn $"Initialized by thread {large.InitializedBy} last used by thread {large.Data[0]}.")
Dim large As LargeObject = lazyLargeObject.Value

' IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the  
'            object after creation. You must lock the object before accessing it,
'            unless the type is thread safe. (LargeObject is not thread safe.)
SyncLock large
    large.Data(0) = Thread.CurrentThread.ManagedThreadId
    Console.WriteLine("Initialized by thread {0}; last used by thread {1}.", _
        large.InitializedBy, large.Data(0))
End SyncLock

コードの最後の LargeObject キー セクションを含むクラスのコンストラクターは、メッセージを表示し、初期化中のスレッドの ID を記録します。 プログラムからの出力は、完全なコード一覧の最後に表示されます。

int initBy = 0;
public LargeObject(int initializedBy)
{
    initBy = initializedBy;
    Console.WriteLine("LargeObject was created on thread id {0}.", initBy);
}
type LargeObject(initBy) =
    do 
        printfn $"LargeObject was created on thread id %i{initBy}."
Private initBy As Integer = 0
Public Sub New(ByVal initializedBy As Integer)
    initBy = initializedBy
    Console.WriteLine("LargeObject was created on thread id {0}.", initBy)
End Sub

注意

簡略化のために、この例では Lazy<T> のグローバル インスタンスを使用して、すべてのメソッドは static (Visual Basic の Shared) です。 これらは、遅延初期化を使用するための要件ではありません。

using System;
using System.Threading;

class Program
{
    static Lazy<LargeObject> lazyLargeObject = null;

    static LargeObject InitLargeObject()
    {
        LargeObject large = new LargeObject(Thread.CurrentThread.ManagedThreadId);
        // Perform additional initialization here.
        return large;
    }

    static void Main()
    {
        // The lazy initializer is created here. LargeObject is not created until the
        // ThreadProc method executes.
        lazyLargeObject = new Lazy<LargeObject>(InitLargeObject);

        // The following lines show how to use other constructors to achieve exactly the
        // same result as the previous line:
        //lazyLargeObject = new Lazy<LargeObject>(InitLargeObject, true);
        //lazyLargeObject = new Lazy<LargeObject>(InitLargeObject,
        //                               LazyThreadSafetyMode.ExecutionAndPublication);

        Console.WriteLine(
            "\r\nLargeObject is not created until you access the Value property of the lazy" +
            "\r\ninitializer. Press Enter to create LargeObject.");
        Console.ReadLine();

        // Create and start 3 threads, each of which uses LargeObject.
        Thread[] threads = new Thread[3];
        for (int i = 0; i < 3; i++)
        {
            threads[i] = new Thread(ThreadProc);
            threads[i].Start();
        }

        // Wait for all 3 threads to finish.
        foreach (Thread t in threads)
        {
            t.Join();
        }

        Console.WriteLine("\r\nPress Enter to end the program");
        Console.ReadLine();
    }

    static void ThreadProc(object state)
    {
        LargeObject large = lazyLargeObject.Value;

        // IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
        //            object after creation. You must lock the object before accessing it,
        //            unless the type is thread safe. (LargeObject is not thread safe.)
        lock(large)
        {
            large.Data[0] = Thread.CurrentThread.ManagedThreadId;
            Console.WriteLine("Initialized by thread {0}; last used by thread {1}.",
                large.InitializedBy, large.Data[0]);
        }
    }
}

class LargeObject
{
    public int InitializedBy { get { return initBy; } }

    int initBy = 0;
    public LargeObject(int initializedBy)
    {
        initBy = initializedBy;
        Console.WriteLine("LargeObject was created on thread id {0}.", initBy);
    }

    public long[] Data = new long[100000000];
}

/* This example produces output similar to the following:

LargeObject is not created until you access the Value property of the lazy
initializer. Press Enter to create LargeObject.

LargeObject was created on thread id 3.
Initialized by thread 3; last used by thread 3.
Initialized by thread 3; last used by thread 4.
Initialized by thread 3; last used by thread 5.

Press Enter to end the program
 */
open System
open System.Threading

type LargeObject(initBy) =
    do 
        printfn $"LargeObject was created on thread id %i{initBy}."
    member _.InitializedBy = initBy
    member val Data = Array.zeroCreate<int64> 100000000

let initLargeObject () =
    let large = LargeObject Thread.CurrentThread.ManagedThreadId
    // Perform additional initialization here.
    large

// The lazy initializer is created here. LargeObject is not created until the
// ThreadProc method executes.
let lazyLargeObject = Lazy<LargeObject> initLargeObject

// The following lines show how to use other constructors to achieve exactly the
// same result as the previous line:
//     let lazyLargeObject = Lazy<LargeObject>(initLargeObject, true)
//     let lazyLargeObject = Lazy<LargeObject>(initLargeObject,
//                               LazyThreadSafetyMode.ExecutionAndPublication)

let threadProc (state: obj) =
    let large = lazyLargeObject.Value

    // IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the
    //            object after creation. You must lock the object before accessing it,
    //            unless the type is thread safe. (LargeObject is not thread safe.)
    lock large (fun () ->
        large.Data[0] <- Thread.CurrentThread.ManagedThreadId
        printfn $"Initialized by thread {large.InitializedBy} last used by thread {large.Data[0]}.")

printfn """
LargeObject is not created until you access the Value property of the lazy
initializer. Press Enter to create LargeObject."""
stdin.ReadLine() |> ignore

// Create and start 3 threads, each of which uses LargeObject.

let threads = Array.zeroCreate 3
for i = 0 to 2 do
    threads[i] <- Thread(ParameterizedThreadStart threadProc)
    threads[i].Start()

// Wait for all 3 threads to finish.
for t in threads do
    t.Join()

printfn "\nPress Enter to end the program"
stdin.ReadLine() |> ignore

// This example produces output similar to the following:
//     LargeObject is not created until you access the Value property of the lazy
//     initializer. Press Enter to create LargeObject.
//     
//     LargeObject was created on thread id 3.
//     Initialized by thread 3 last used by thread 3.
//     Initialized by thread 3 last used by thread 4.
//     Initialized by thread 3 last used by thread 5.
//     
//     Press Enter to end the program
Imports System.Threading

Friend Class Program
    Private Shared lazyLargeObject As Lazy(Of LargeObject) = Nothing

    Private Shared Function InitLargeObject() As LargeObject
        Dim large As New LargeObject(Thread.CurrentThread.ManagedThreadId)
        ' Perform additional initialization here.
        Return large
    End Function


    Shared Sub Main()
        ' The lazy initializer is created here. LargeObject is not created until the 
        ' ThreadProc method executes.
        lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject)

        ' The following lines show how to use other constructors to achieve exactly the
        ' same result as the previous line: 
        'lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject, True)
        'lazyLargeObject = New Lazy(Of LargeObject)(AddressOf InitLargeObject, _
        '                               LazyThreadSafetyMode.ExecutionAndPublication)


        Console.WriteLine(vbCrLf & _
            "LargeObject is not created until you access the Value property of the lazy" _
            & vbCrLf & "initializer. Press Enter to create LargeObject.")
        Console.ReadLine()

        ' Create and start 3 threads, each of which uses LargeObject.
        Dim threads(2) As Thread
        For i As Integer = 0 To 2
            threads(i) = New Thread(AddressOf ThreadProc)
            threads(i).Start()
        Next i

        ' Wait for all 3 threads to finish. 
        For Each t As Thread In threads
            t.Join()
        Next t

        Console.WriteLine(vbCrLf & "Press Enter to end the program")
        Console.ReadLine()
    End Sub


    Private Shared Sub ThreadProc(ByVal state As Object)
        Dim large As LargeObject = lazyLargeObject.Value

        ' IMPORTANT: Lazy initialization is thread-safe, but it doesn't protect the  
        '            object after creation. You must lock the object before accessing it,
        '            unless the type is thread safe. (LargeObject is not thread safe.)
        SyncLock large
            large.Data(0) = Thread.CurrentThread.ManagedThreadId
            Console.WriteLine("Initialized by thread {0}; last used by thread {1}.", _
                large.InitializedBy, large.Data(0))
        End SyncLock
    End Sub
End Class

Friend Class LargeObject
    Public ReadOnly Property InitializedBy() As Integer
        Get
            Return initBy
        End Get
    End Property

    Private initBy As Integer = 0
    Public Sub New(ByVal initializedBy As Integer)
        initBy = initializedBy
        Console.WriteLine("LargeObject was created on thread id {0}.", initBy)
    End Sub

    Public Data(99999999) As Long
End Class

' This example produces output similar to the following:
'
'LargeObject is not created until you access the Value property of the lazy
'initializer. Press Enter to create LargeObject.
'
'LargeObject was created on thread id 3.
'Initialized by thread 3; last used by thread 3.
'Initialized by thread 3; last used by thread 5.
'Initialized by thread 3; last used by thread 4.
'
'Press Enter to end the program
'

注釈

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

遅延初期化を準備するには、次の Lazy<T>インスタンスを作成します。 作成するオブジェクトの Lazy<T> 型引数は、遅延を初期化するオブジェクトの型を指定します。 オブジェクトの作成 Lazy<T> に使用するコンストラクターによって、初期化の特性が決まります。 遅延初期化は、Lazy<T>.Value プロパティへの初回のアクセス時に発生します。

ほとんどの場合、コンストラクターの選択は、次の 2 つの質問に対する回答によって異なります。

  • 遅延初期化されたオブジェクトは、複数のスレッドからアクセスされますか? その場合、オブジェクトは任意の Lazy<T> スレッドで作成できます。 既定の動作でスレッド セーフ Lazy<T> オブジェクトを作成する単純なコンストラクターの 1 つを使用して、遅延インスタンス化されたオブジェクトのインスタンスがいくつのスレッドでアクセスしようとしても作成されるようにすることができます。 スレッド セーフではないオブジェクトを Lazy<T> 作成するには、スレッド セーフを指定できないコンストラクターを使用する必要があります。

    注意事項

    オブジェクト スレッドを Lazy<T> 安全にしても、遅延初期化されたオブジェクトは保護されません。 遅延初期化されたオブジェクトに複数のスレッドがアクセスできる場合は、そのプロパティとメソッドをマルチスレッド アクセスに対して安全にする必要があります。

  • 遅延初期化には多くのコードが必要ですか、または遅延初期化されたオブジェクトには、必要なものをすべて実行し、例外をスローしないパラメーターなしのコンストラクターがありますか? 初期化コードを記述する必要がある場合、または例外を処理する必要がある場合は、ファクトリ メソッドを受け取るコンストラクターのいずれかを使用します。 ファクトリ メソッドで初期化コードを記述します。

次の表は、これらの 2 つの要因に基づいて、選択するコンストラクターを示しています。

オブジェクトは、次の方法でアクセスされます。 初期化コードが必要ない場合 (パラメーターなしのコンストラクター)、次のコマンドを使用します。 初期化コードが必要な場合は、
複数のスレッド Lazy<T>() Lazy<T>(Func<T>)
1 つのスレッド isThreadSafefalse に設定された Lazy<T>(Boolean) isThreadSafefalse に設定された Lazy<T>(Func<T>, Boolean)

ラムダ式を使用して、ファクトリ メソッドを指定できます。 これにより、すべての初期化コードが 1 か所に保持されます。 ラムダ式は、遅延初期化されたオブジェクトのコンストラクターに渡す引数を含め、コンテキストをキャプチャします。

例外キャッシュ ファクトリ メソッドを使用すると、例外がキャッシュされます。 つまり、スレッドがオブジェクトのプロパティLazy<T>に初めてアクセスしようとしたときにファクトリ メソッドが例外をValueスローした場合、後続の試行ごとに同じ例外がスローされます。 これにより、プロパティを Value 呼び出すたびに同じ結果が生成され、異なるスレッドが異なる結果を得た場合に発生する可能性のある微妙なエラーを回避できます。 これは Lazy<T> 、それ以外の場合は、以前の時点 (通常は起動時) に初期化された実際 T を表します。 その前の時点での障害は、通常致命的です。 回復可能なエラーが発生する可能性がある場合は、遅延初期化を使用していない場合と同様に、初期化ルーチン (この場合はファクトリ メソッド) に再試行ロジックをビルドすることをお勧めします。

ロックに代わる方法 特定の状況では、オブジェクトの既定のロック動作の Lazy<T> オーバーヘッドを回避することが必要な場合があります。 まれな状況では、デッドロックが発生する可能性があります。 このような場合は、またはLazy<T>(Func<T>, LazyThreadSafetyMode)コンストラクターをLazy<T>(LazyThreadSafetyMode)使用してLazyThreadSafetyMode.PublicationOnly指定できます。 これにより、スレッドがプロパティを Lazy<T> 同時に呼び出す場合、オブジェクトは複数のスレッドごとに遅延初期化されたオブジェクトのコピーを Value 作成できます。 このオブジェクトは Lazy<T> 、すべてのスレッドが遅延初期化オブジェクトの同じインスタンスを使用し、使用されていないインスタンスを破棄することを保証します。 したがって、ロックオーバーヘッドを削減するコストは、プログラムが高価なオブジェクトの余分なコピーを作成して破棄する場合があるということです。 ほとんどの場合、これは可能性が低いです。 コンストラクターのLazy<T>(Func<T>, LazyThreadSafetyMode)例では、Lazy<T>(LazyThreadSafetyMode)この動作を示します。

重要

指定すると、ファクトリ メソッドを指定 LazyThreadSafetyMode.PublicationOnlyした場合でも、例外はキャッシュされません。

同等のコンストラクター 使用を LazyThreadSafetyMode.PublicationOnly有効にするだけでなく、 Lazy<T>(LazyThreadSafetyMode) コンストラクターと Lazy<T>(Func<T>, LazyThreadSafetyMode) コンストラクターは他のコンストラクターの機能を複製することもできます。 次の表は、同等の動作を生成するパラメーター値を示しています。

次のオブジェクトを Lazy<T> 作成するには パラメーターを持つコンストラクターの場合は、次にLazyThreadSafetyMode``mode設定modeします。 ブール値 isThreadSafe パラメーターを持つコンストラクターの場合は、次に設定 isThreadSafe します。 スレッド セーフ パラメーターのないコンストラクターの場合
完全にスレッド セーフです。では、ロックを使用して、値を初期化するスレッドが 1 つだけであることを確認します。 ExecutionAndPublication true このようなコンストラクターはすべて、完全にスレッド セーフです。
スレッド セーフではありません。 None false 適用不可。
完全にスレッド セーフです。スレッドは値を初期化するために競合します。 PublicationOnly 適用不可。 適用不可。

その他の機能 スレッド静的フィールドでの使用、またはプロパティの Lazy<T> バッキング ストアとしての使用については、「 遅延初期化」を参照してください。

コンストラクター

Lazy<T>()

Lazy<T> クラスの新しいインスタンスを初期化します。 遅延初期化が発生した場合、ターゲットの型のパラメーターなしのコンストラクターが使用されます。

Lazy<T>(Boolean)

Lazy<T> クラスの新しいインスタンスを初期化します。 遅延初期化が発生した場合、ターゲットの型のパラメーターなしのコンストラクターと指定された初期化モードが使用されます。

Lazy<T>(Func<T>)

Lazy<T> クラスの新しいインスタンスを初期化します。 遅延初期化が発生すると、指定された初期化関数が使用されます。

Lazy<T>(Func<T>, Boolean)

Lazy<T> クラスの新しいインスタンスを初期化します。 遅延初期化が発生すると、指定された初期化関数と初期化モードが使用されます。

Lazy<T>(Func<T>, LazyThreadSafetyMode)

指定された初期化関数とスレッド セーフ モードを使用する Lazy<T> クラスの新しいインスタンスを初期化します。

Lazy<T>(LazyThreadSafetyMode)

T のパラメーターなしのコンストラクターと指定されたスレッド セーフ モードを使用する Lazy<T> クラスの新しいインスタンスを初期化します。

Lazy<T>(T)

事前に初期化され、指定された値を使用する Lazy<T> クラスの新しいインスタンスを初期化します。

プロパティ

IsValueCreated

この Lazy<T> インスタンスに対して値が作成されているかどうかを示す値を取得します。

Value

現在の Lazy<T> インスタンスの遅れて初期化される値を取得します。

メソッド

Equals(Object)

指定されたオブジェクトが現在のオブジェクトと等しいかどうかを判断します。

(継承元 Object)
GetHashCode()

既定のハッシュ関数として機能します。

(継承元 Object)
GetType()

現在のインスタンスの Type を取得します。

(継承元 Object)
MemberwiseClone()

現在の Object の簡易コピーを作成します。

(継承元 Object)
ToString()

このインスタンスの Value プロパティの文字列形式を作成して返します。

適用対象

スレッド セーフ

既定では、クラスのすべてのパブリック メンバーと保護メンバー Lazy<T> はスレッド セーフであり、複数のスレッドから同時に使用できます。 これらのスレッド セーフの保証は、型のコンストラクターに対するパラメーターを使用して、必要に応じてインスタンスごとに削除できます。

こちらもご覧ください