次の方法で共有


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>, Boolean) コンストラクター (isThreadSafetrue を指定する) と Lazy<T>(Func<T>, LazyThreadSafetyMode) コンストラクター (modeLazyThreadSafetyMode.ExecutionAndPublication を指定する) の使用方法も示します。 別のコンストラクターに切り替えるには、コメント アウトするコンストラクターを変更するだけです。

同じコンストラクターを使用した例外キャッシュの例については、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> オブジェクトを作成する単純なコンストラクターのいずれかを使用して、遅延インスタンス化されたオブジェクトのインスタンスがいくつのスレッドがアクセスしようとしても作成されるようにすることができます。 スレッド セーフではない 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>(LazyThreadSafetyMode) または Lazy<T>(Func<T>, LazyThreadSafetyMode) コンストラクターを使用し、LazyThreadSafetyMode.PublicationOnlyを指定できます。 これにより、スレッドが Value プロパティを同時に呼び出す場合、Lazy<T> オブジェクトは、複数のスレッドごとに遅延初期化オブジェクトのコピーを作成できます。 Lazy<T> オブジェクトは、すべてのスレッドが遅延初期化オブジェクトの同じインスタンスを使用し、使用されていないインスタンスを破棄することを保証します。 したがって、ロックオーバーヘッドを削減するコストは、プログラムが高価なオブジェクトの余分なコピーを作成して破棄する場合があるということです。 ほとんどの場合、これは可能性が低いです。 Lazy<T>(LazyThreadSafetyMode) コンストラクターと Lazy<T>(Func<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>(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> クラスのすべてのパブリック メンバーとプロテクト メンバーはスレッド セーフであり、複数のスレッドから同時に使用できます。 これらのスレッド セーフの保証は、型のコンストラクターに対するパラメーターを使用して、必要に応じてインスタンスごとに削除できます。

こちらもご覧ください