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>) 构造函数。 它还演示了为) 指定的true构造函数 (用法Lazy<T>(Func<T>, Boolean),以及Lazy<T>(Func<T>, LazyThreadSafetyMode)mode) 指定的LazyThreadSafetyMode.ExecutionAndPublication构造函数 (。isThreadSafe 若要切换到其他构造函数,只需更改注释掉哪些构造函数。

有关使用相同构造函数演示异常缓存的示例,请参阅 Lazy<T>(Func<T>) 构造函数。

此示例定义一个将由多个线程之一延迟初始化的 LargeObject 类。 代码的四个关键部分说明了如何创建初始值设定项、工厂方法、实际初始化和类的 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

请注意,前两个代码节可以使用 lambda 函数组合,如下所示:

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 键时,该示例将创建并启动三个线程。 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 构造函数(包括代码的最后一个关键部分)显示消息并记录初始化线程的标识。 程序输出显示在完整代码列表的末尾。

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 属性时出现延迟初始化。

在大多数情况下,选择构造函数取决于两个问题的解答:

  • 是否会从多个线程访问延迟初始化的对象? 如果是这样,该 Lazy<T> 对象可能会在任何线程上创建它。 可以使用其默认行为是创建线程安全 Lazy<T> 对象的简单构造函数之一,因此无论有多少线程尝试访问该对象,都只创建一个延迟实例化对象的实例。 若要创建 Lazy<T> 不是线程安全的对象,必须使用一个构造函数来指定没有线程安全性。

    注意

    Lazy<T>使对象线程安全不会保护延迟初始化的对象。 如果多个线程可以访问延迟初始化的对象,则必须使其属性和方法安全进行多线程访问。

  • 延迟初始化是否需要大量代码,或者延迟初始化的对象是否具有无参数构造函数,该构造函数会执行所需的一切操作,并且不会引发异常? 如果需要编写初始化代码或需要处理异常,请使用采用工厂方法的构造函数之一。 在工厂方法中编写初始化代码。

下表根据以下两个因素显示了要选择的构造函数:

对象将由 如果没有初始化代码 (无参数构造函数) ,请使用 如果需要初始化代码,请使用
多个线程 Lazy<T>() Lazy<T>(Func<T>)
一个线程 Lazy<T>(Boolean),其中 isThreadSafe 设置为 false Lazy<T>(Func<T>, Boolean),其中 isThreadSafe 设置为 false

可以使用 lambda 表达式来指定工厂方法。 这会将所有初始化代码保留在一个位置。 lambda 表达式捕获上下文,包括传递给延迟初始化对象的构造函数的任何参数。

异常缓存 使用工厂方法时,会缓存异常。 也就是说,如果工厂方法首次尝试访问 Value 对象的属性 Lazy<T> 时引发异常,则每次后续尝试时都会引发相同的异常。 这可确保对属性的每个调用 Value 都会生成相同的结果,并避免在不同线程获得不同结果时可能出现的细微错误。 代表 Lazy<T> 实际 T 值,否则在一些早期点(通常在启动期间)初始化。 早点的故障通常是致命的。 如果可能存在可恢复故障,我们建议在本例中将重试逻辑构建到初始化例程 (,工厂方法) ,就像你没有使用延迟初始化一样。

锁定替代方法 在某些情况下,你可能希望避免对象的默认锁定行为的开销 Lazy<T> 。 在极少数情况下,可能存在死锁。 在这种情况下,可以使用 Lazy<T>(LazyThreadSafetyMode)Lazy<T>(Func<T>, LazyThreadSafetyMode) 构造函数并指定 LazyThreadSafetyMode.PublicationOnly。 这样, Lazy<T> 如果线程同时调用 Value 该属性,该对象就可以在多个线程的每个线程上创建延迟初始化对象的副本。 该 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 对于没有线程安全参数的构造函数
完全线程安全;使用锁定来确保只有一个线程初始化该值。 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)

初始化 Lazy<T> 类的新实例,其中使用 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> 都是线程安全的,并且可以从多个线程并发使用。 可以使用类型构造函数的参数(可选)和每个实例删除这些线程安全保证。

另请参阅