Lazy<T> 類別

定義

提供延遲初始設定的支援。Provides support for lazy initialization.

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

類型參數

T

要延遲初始化之物件的類型。The type of object that is being lazily initialized.

繼承
Lazy<T>
衍生
屬性

範例

下列範例示範如何使用Lazy<T>類別來提供延遲初始化, 以及從多個執行緒存取。The following example demonstrates the use of the Lazy<T> class to provide lazy initialization with access from multiple threads.

注意

此範例會使用Lazy<T>(Func<T>)此函數。The example uses the Lazy<T>(Func<T>) constructor. 它也會示範如何使用此Lazy<T>(Func<T>, Boolean)函式 ( true針對isThreadSafe指定) 和Lazy<T>(Func<T>, LazyThreadSafetyMode)函式 ( LazyThreadSafetyMode.ExecutionAndPublication針對mode指定)。It also demonstrates the use of the Lazy<T>(Func<T>, Boolean) constructor (specifying true for isThreadSafe) and the Lazy<T>(Func<T>, LazyThreadSafetyMode) constructor (specifying LazyThreadSafetyMode.ExecutionAndPublication for mode). 若要切換至不同的函式, 只需變更已批註化的函式。To switch to a different constructor, just change which constructors are commented out.

如需使用相同的函式來示範例外狀況快取的Lazy<T>(Func<T>)範例, 請參閱函式。For an example that demonstrates exception caching using the same constructors, see the Lazy<T>(Func<T>) constructor.

這個範例定義將由多個執行緒中的一個執行延遲初始化的 LargeObject 類別。The example defines a LargeObject class that will be initialized lazily by one of several threads. 程式碼的四個主要區段說明如何建立初始化運算式、factory 方法、實際的LargeObject初始化, 以及類別的函式, 這會在建立物件時顯示訊息。The four key sections of code illustrate the creation of the initializer, the factory method, the actual initialization, and the constructor of the LargeObject class, which displays a message when the object is created. Main 方法的開頭,此範例會為 LargeObject 建立安全執行緒延遲初始設定式:At the beginning of the Main method, the example creates the thread-safe lazy initializer for 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);
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)

Factory 方法會顯示物件的建立, 並具有用於進一步初始化的預留位置:The factory method shows the creation of the object, with a placeholder for further initialization:

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

請注意, 前兩個程式碼區段可以使用 lambda 函數結合, 如下所示:Note that the first two code sections could be combined by using a lambda function, as shown here:

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

此範例會暫停, 以指出延遲初始化發生之前, 可能會經過不確定的期間。The example pauses, to indicate that an indeterminate period may elapse before lazy initialization occurs. 當您按下enter鍵時, 此範例會建立並啟動三個執行緒。When you press the Enter key, the example creates and starts three threads. 所有三個執行緒所使用的Value 方法都會呼叫屬性。ThreadProcThe ThreadProc method that's used by all three threads calls the Value property. 第一次發生這種情況LargeObject時, 會建立實例:The first time this happens, the LargeObject instance is created:

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]);
}
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類別的函式 (包括程式碼的最後一個索引鍵區段) 會顯示訊息, 並記錄初始化執行緒的身分識別。The constructor of the LargeObject class, which includes the last key section of code, displays a message and records the identity of the initializing thread. 程式的輸出會出現在完整程式代碼清單的結尾。The output from the program appears at the end of the full code listing.

int initBy = 0;
public LargeObject(int initializedBy)
{
    initBy = initializedBy;
    Console.WriteLine("LargeObject was created on thread id {0}.", 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)。For simplicity, this example uses a global instance of Lazy<T>, and all the methods are static (Shared in Visual Basic). 這些不是使用延遲初始設定的必要項。These are not requirements for the use of lazy initialization.

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
 */
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
' 

備註

使用延遲初始初始化來延遲建立大型或耗用大量資源的物件, 或執行大量資源的工作, 特別是在程式存留期間可能不會發生這類建立或執行的情況。Use lazy initialization to defer the creation of a large or resource-intensive object, or the execution of a resource-intensive task, particularly when such creation or execution might not occur during the lifetime of the program.

若要準備延遲初始化, 請建立的實例Lazy<T>To prepare for lazy initialization, you create an instance of Lazy<T>. 您所建立Lazy<T>物件的型別引數會指定您想要延遲初始化之物件的型別。The type argument of the Lazy<T> object that you create specifies the type of the object that you want to initialize lazily. 您用來建立Lazy<T>物件的構造函式會決定初始化的特性。The constructor that you use to create the Lazy<T> object determines the characteristics of the initialization. 延遲初始化會在第一次存取 Lazy<T>.Value 屬性時發生。Lazy initialization occurs the first time the Lazy<T>.Value property is accessed.

在大部分的情況下, 選擇一個「定序」取決於您對兩個問題的答案:In most cases, choosing a constructor depends on your answers to two questions:

  • 延遲初始化的物件是否可以從一個以上的執行緒存取?Will the lazily initialized object be accessed from more than one thread? 若是如此, Lazy<T>物件可能會在任何執行緒上建立。If so, the Lazy<T> object might create it on any thread. 您可以使用其中一個簡單的函式, 其預設行為是建立安全Lazy<T>執行緒的物件, 因此無論有多少執行緒嘗試存取它, 都只會建立一個延遲具現化物件的實例。You can use one of the simple constructors whose default behavior is to create a thread-safe Lazy<T> object, so that only one instance of the lazily instantiated object is created no matter how many threads try to access it. 若要建立Lazy<T>不是安全線程的物件, 您必須使用可讓您指定無線程安全性的函式。To create a Lazy<T> object that is not thread safe, you must use a constructor that enables you to specify no thread safety.

    警告

    Lazy<T>使物件具備執行緒安全, 並不會保護延遲初始化的物件。Making the Lazy<T> object thread safe does not protect the lazily initialized object. 如果多個執行緒可以存取延遲初始化的物件, 您必須讓其屬性和方法安全地進行多執行緒存取。If multiple threads can access the lazily initialized object, you must make its properties and methods safe for multithreaded access.

  • 延遲初始化需要大量的程式碼, 還是延遲初始化的物件有無參數的函式, 可執行您所需的所有專案, 而不會擲回例外狀況?Does lazy initialization require a lot of code, or does the lazily initialized object have a parameterless constructor that does everything you need and doesn't throw exceptions? 如果您需要撰寫初始化程式碼, 或需要處理例外狀況, 請使用其中一個採用 factory 方法的函式。If you need to write initialization code or if exceptions need to be handled, use one of the constructors that takes a factory method. 在 factory 方法中撰寫您的初始化程式碼。Write your initialization code in the factory method.

下表根據下列兩個因素, 顯示要選擇的構造函式:The following table shows which constructor to choose, based on these two factors:

物件將由存取Object will be accessed by 如果不需要任何初始化程式碼 (無參數的函式), 請使用If no initialization code is required (parameterless constructor), use 如果需要初始化程式碼, 請使用If initialization code is required, use
多個執行緒Multiple threads Lazy<T>() Lazy<T>(Func<T>)
一個執行緒One thread Lazy<T>(Boolean), isThreadSafe其設定false為。Lazy<T>(Boolean) with isThreadSafe set to false. Lazy<T>(Func<T>, Boolean), isThreadSafe其設定false為。Lazy<T>(Func<T>, Boolean) with isThreadSafe set to false.

您可以使用 lambda 運算式來指定 factory 方法。You can use a lambda expression to specify the factory method. 這會將所有初始化程式碼保留在同一個位置。This keeps all the initialization code in one place. Lambda 運算式會捕捉內容, 包括您傳遞給延遲初始化物件之函式的任何引數。The lambda expression captures the context, including any arguments you pass to the lazily initialized object's constructor.

例外狀況快取當您使用 factory 方法時, 會快取例外狀況。Exception caching When you use factory methods, exceptions are cached. 也就是說, 如果 factory 方法在第一次嘗試存取Value Lazy<T>物件的屬性時擲回例外狀況, 則每次後續的嘗試都會擲回相同的例外狀況。That is, if the factory method throws an exception the first time a thread tries to access the Value property of the Lazy<T> object, the same exception is thrown on every subsequent attempt. 這可確保每次呼叫Value屬性都會產生相同的結果, 並避免在不同的執行緒取得不同的結果時可能會發生的細微錯誤。This ensures that every call to the Value property produces the same result and avoids subtle errors that might arise if different threads get different results. 實際上, T這表示在某些較早的時間點 (通常是在啟動期間) 初始化的情況。 Lazy<T>The Lazy<T> stands in for an actual T that otherwise would have been initialized at some earlier point, usually during startup. 這個較早的時間點的失敗通常是嚴重的。A failure at that earlier point is usually fatal. 如果可能發生可復原的失敗, 建議您將重試邏輯建立到初始化常式 (在此案例中為 factory 方法), 就像您不使用延遲初始化一樣。If there is a potential for a recoverable failure, we recommend that you build the retry logic into the initialization routine (in this case, the factory method), just as you would if you weren't using lazy initialization.

鎖定的替代方法在某些情況下, 您可能會想要避免Lazy<T>物件的預設鎖定行為的額外負荷。Alternative to locking In certain situations, you might want to avoid the overhead of the Lazy<T> object's default locking behavior. 在罕見的情況下, 可能會有鎖死的可能性。In rare situations, there might be a potential for deadlocks. 在這種情況下, 您可以Lazy<T>(LazyThreadSafetyMode)使用Lazy<T>(Func<T>, LazyThreadSafetyMode)或函數, 並LazyThreadSafetyMode.PublicationOnly指定。In such cases, you can use the Lazy<T>(LazyThreadSafetyMode) or Lazy<T>(Func<T>, LazyThreadSafetyMode) constructor, and specify LazyThreadSafetyMode.PublicationOnly. 這可讓Lazy<T>物件在多個執行緒上建立延遲初始化物件的複本 (如果執行緒同時Value呼叫屬性的話)。This enables the Lazy<T> object to create a copy of the lazily initialized object on each of several threads if the threads call the Value property simultaneously. Lazy<T>物件可確保所有線程都使用延遲初始化物件的相同實例, 並捨棄未使用的實例。The Lazy<T> object ensures that all threads use the same instance of the lazily initialized object and discards the instances that are not used. 因此, 降低鎖定額外負荷的代價是, 您的程式有時可能會建立並捨棄昂貴物件的額外複本。Thus, the cost of reducing the locking overhead is that your program might sometimes create and discard extra copies of an expensive object. 在大部分情況下, 這不太可能發生。In most cases, this is unlikely. Lazy<T>(LazyThreadSafetyMode)Lazy<T>(Func<T>, LazyThreadSafetyMode)函式的範例會示範此行為。The examples for the Lazy<T>(LazyThreadSafetyMode) and Lazy<T>(Func<T>, LazyThreadSafetyMode) constructors demonstrate this behavior.

重要

當您指定LazyThreadSafetyMode.PublicationOnly時, 永遠不會快取例外狀況, 即使您指定 factory 方法也一樣。When you specify LazyThreadSafetyMode.PublicationOnly, exceptions are never cached, even if you specify a factory method.

等的構造函式除了啟用的使用LazyThreadSafetyMode.PublicationOnly之外Lazy<T>(LazyThreadSafetyMode) , 和Lazy<T>(Func<T>, LazyThreadSafetyMode)函式也可以複製其他函式的功能。Equivalent constructors In addition to enabling the use of LazyThreadSafetyMode.PublicationOnly, the Lazy<T>(LazyThreadSafetyMode) and Lazy<T>(Func<T>, LazyThreadSafetyMode) constructors can duplicate the functionality of the other constructors. 下表顯示產生對等行為的參數值。The following table shows the parameter values that produce equivalent behavior.

若要建立Lazy<T>物件, 其為To create a Lazy<T> object that is 針對具有LazyThreadSafetyMode mode參數的函式, 請mode將設定為For constructors that have a LazyThreadSafetyMode mode parameter, set mode to 若為具有布林值isThreadSafe參數的函式, 請將設定為isThreadSafeFor constructors that have a Boolean isThreadSafe parameter, set isThreadSafe to 適用于沒有線程安全參數的函式For constructors with no thread safety parameters
完全安全線程;使用鎖定來確保只有一個執行緒會初始化此值。Fully thread safe; uses locking to ensure that only one thread initializes the value. ExecutionAndPublication true 所有這類的函式都是完全安全線程。All such constructors are fully thread safe.
不具備執行緒安全。Not thread safe. None false 不適用。Not applicable.
完全安全線程;將值初始化的執行緒競爭。Fully thread safe; threads race to initialize the value. PublicationOnly 不適用。Not applicable. 不適用。Not applicable.

其他功能如需使用Lazy<T>和執行緒靜態欄位, 或做為屬性之備份存放區的詳細資訊, 請參閱延遲初始化Other capabilities For information about the use of Lazy<T> with thread-static fields, or as the backing store for properties, see Lazy Initialization.

建構函式

Lazy<T>()

初始化 Lazy<T> 類別的新執行個體。Initializes a new instance of the Lazy<T> class. 當發生延遲初始設定時,會使用目標類型的無參數建構函式。When lazy initialization occurs, the parameterless constructor of the target type is used.

Lazy<T>(Boolean)

初始化 Lazy<T> 類別的新執行個體。Initializes a new instance of the Lazy<T> class. 當發生延遲初始設定時,會使用目標類型的無參數建構函式和所指定初始設定模式。When lazy initialization occurs, the parameterless constructor of the target type and the specified initialization mode are used.

Lazy<T>(Func<T>)

初始化 Lazy<T> 類別的新執行個體。Initializes a new instance of the Lazy<T> class. 當延遲初始設定發生時,會使用指定的初始設定函式。When lazy initialization occurs, the specified initialization function is used.

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

初始化 Lazy<T> 類別的新執行個體。Initializes a new instance of the Lazy<T> class. 當延遲初始設定發生時,會使用指定的初始設定函式和初始設定模式。When lazy initialization occurs, the specified initialization function and initialization mode are used.

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

初始化 Lazy<T> 類別的新行個體,這個執行個體會使用指定的初始設定函式和執行緒安全模式。Initializes a new instance of the Lazy<T> class that uses the specified initialization function and thread-safety mode.

Lazy<T>(LazyThreadSafetyMode)

初始化 Lazy<T> 類別的新執行個體,這個執行個體會使用 T 的無參數建構函式和所指定執行緒安全模式。Initializes a new instance of the Lazy<T> class that uses the parameterless constructor of T and the specified thread-safety mode.

Lazy<T>(T)

將使用預先初始化指定值之 Lazy<T> 類別的執行個體初始化。Initializes a new instance of the Lazy<T> class that uses a preinitialized specified value.

屬性

IsValueCreated

取得值,這個值表示是否已建立此 Lazy<T> 執行個體的值。Gets a value that indicates whether a value has been created for this Lazy<T> instance.

Value

取得目前 Lazy<T> 執行個體的延遲初始化值。Gets the lazily initialized value of the current Lazy<T> instance.

方法

Equals(Object)

判斷指定的物件是否等於目前的物件。Determines whether the specified object is equal to the current object.

(繼承來源 Object)
GetHashCode()

做為預設雜湊函式。Serves as the default hash function.

(繼承來源 Object)
GetType()

取得目前執行個體的 TypeGets the Type of the current instance.

(繼承來源 Object)
MemberwiseClone()

建立目前 Object 的淺層複本 (Shallow Copy)。Creates a shallow copy of the current Object.

(繼承來源 Object)
ToString()

建立並傳回這個執行個體之 Value 屬性的字串表示。Creates and returns a string representation of the Value property for this instance.

適用於

執行緒安全性

根據預設, Lazy<T>類別的所有公開和受保護成員都是安全線程, 而且可以從多個執行緒同時使用。By default, all public and protected members of the Lazy<T> class are thread safe and may be used concurrently from multiple threads. 您可以選擇性地移除這些執行緒安全保證, 並在每個實例上使用參數, 將其用於類型的函式。These thread-safety guarantees may be removed optionally and per instance, using parameters to the type's constructors.

另請參閱