Lazy<T> 类

定义

提供对延迟初始化的支持。Provides support for lazy initialization.

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

正在延迟初始化的对象的类型。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 modeIt 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. 代码的四个关键部分说明了如何创建初始值设定项、工厂方法、实际初始化和类的构造函数,在 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)

工厂方法显示对象的创建,其中包含一个用于进一步初始化的占位符: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. ThreadProc所有三个线程使用的方法都调用 Value 属性。The 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? 如果需要编写初始化代码或需要处理异常,请使用采用工厂方法的构造函数之一。If you need to write initialization code or if exceptions need to be handled, use one of the constructors that takes a factory method. 在工厂方法中编写初始化代码。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 设置为,则为 falseLazy<T>(Boolean) with isThreadSafe set to false. Lazy<T>(Func<T>, Boolean) 如果 isThreadSafe 设置为,则为 falseLazy<T>(Func<T>, Boolean) with isThreadSafe set to false.

您可以使用 lambda 表达式来指定工厂方法。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.

异常缓存 使用工厂方法时,将缓存异常。Exception caching When you use factory methods, exceptions are cached. 也就是说,如果工厂方法在第一次尝试访问对象的属性时引发异常,则 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. Lazy<T>对于 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. 如果有可恢复故障的可能性,则建议将重试逻辑生成到 (在此示例中,即工厂方法) ,就像在不使用延迟初始化的情况下一样。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.PublicationOnlyIn 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 ,则永远不会缓存异常,即使指定了工厂方法也是如此。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 ,将设置 modeFor 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 的浅表副本。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.

另请参阅