Lazy<T> Classe

Definição

Dá suporte à inicialização lenta.

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)

Parâmetros de tipo

T

O tipo de objeto que está sendo inicializado sem pressa.

Herança
Lazy<T>
Derivado
Atributos

Exemplos

O exemplo a seguir demonstra o uso da Lazy<T> classe para fornecer inicialização lenta com acesso de vários threads.

Observação

O exemplo usa o Lazy<T>(Func<T>) construtor. Ele também demonstra o uso do Lazy<T>(Func<T>, Boolean) construtor (especificando true para isThreadSafe) e do Lazy<T>(Func<T>, LazyThreadSafetyMode) construtor (especificando LazyThreadSafetyMode.ExecutionAndPublication para mode). Para alternar para um construtor diferente, basta alterar quais construtores são comentados.

Para obter um exemplo que demonstra o cache de exceção usando os mesmos construtores, consulte o Lazy<T>(Func<T>) construtor.

O exemplo define uma classe LargeObject que será inicializada lentamente por um dos vários threads. As quatro seções principais do código ilustram a criação do inicializador, o método de fábrica, a inicialização real e o construtor da LargeObject classe, que exibe uma mensagem quando o objeto é criado. No início do método Main, o exemplo cria o inicializador thread-safe lento para 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)

O método de fábrica mostra a criação do objeto, com um espaço reservado para inicialização adicional:

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

Observe que as duas primeiras seções de código podem ser combinadas usando uma função lambda, conforme mostrado aqui:

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)

O exemplo pausa, para indicar que um período indeterminado pode passar antes da inicialização lenta ocorrer. Quando você pressiona a tecla Enter , o exemplo cria e inicia três threads. O ThreadProc método usado pelos três threads chama a Value propriedade. Na primeira vez que isso acontecer, a LargeObject instância será criada:

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

O construtor da LargeObject classe, que inclui a última seção de chave do código, exibe uma mensagem e registra a identidade do thread de inicialização. A saída do programa é exibida no final da listagem de código completa.

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

Observação

Para simplificar, este exemplo usa uma instância global de Lazy<T>, e todos os métodos são static (Shared no Visual Basic). Estes não são requisitos para o uso da inicialização lenta.

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
'

Comentários

Use a inicialização lenta para adiar a criação de um objeto grande ou com uso intensivo de recursos ou a execução de uma tarefa com uso intensivo de recursos, especialmente quando essa criação ou execução pode não ocorrer durante o tempo de vida do programa.

Para se preparar para a inicialização lenta, crie uma instância de Lazy<T>. O argumento de tipo do Lazy<T> objeto que você cria especifica o tipo do objeto que você deseja inicializar preguiçosamente. O construtor que você usa para criar o Lazy<T> objeto determina as características da inicialização. A inicialização lenta ocorre na primeira vez que a propriedade Lazy<T>.Value é acessada.

Na maioria dos casos, escolher um construtor depende de suas respostas para duas perguntas:

  • O objeto inicializado preguiçosamente será acessado de mais de um thread? Nesse caso, o Lazy<T> objeto poderá criá-lo em qualquer thread. Você pode usar um dos construtores simples cujo comportamento padrão é criar um objeto thread-safe Lazy<T> , para que apenas uma instância do objeto preguiçosamente instanciado seja criada independentemente de quantos threads tentem acessá-lo. Para criar um Lazy<T> objeto que não seja thread safe, você deve usar um construtor que permita que você não especifique nenhuma segurança de thread.

    Cuidado

    Tornar o thread de Lazy<T> objeto seguro não protege o objeto inicializado preguiçosamente. Se vários threads puderem acessar o objeto inicializado preguiçosamente, você deverá tornar suas propriedades e métodos seguros para acesso multithread.

  • A inicialização lenta requer muito código ou o objeto inicializado preguiçosamente tem um construtor sem parâmetros que faz tudo o que você precisa e não gera exceções? Se você precisar escrever o código de inicialização ou se as exceções precisarem ser tratadas, use um dos construtores que usa um método de fábrica. Escreva seu código de inicialização no método de fábrica.

A tabela a seguir mostra qual construtor escolher, com base nesses dois fatores:

O objeto será acessado por Se nenhum código de inicialização for necessário (construtor sem parâmetros), use Se o código de inicialização for necessário, use
Vários threads Lazy<T>() Lazy<T>(Func<T>)
Um thread Lazy<T>(Boolean) com definido como isThreadSafe false. Lazy<T>(Func<T>, Boolean) com definido como isThreadSafe false.

Você pode usar uma expressão lambda para especificar o método de fábrica. Isso mantém todo o código de inicialização em um só lugar. A expressão lambda captura o contexto, incluindo todos os argumentos que você passa para o construtor do objeto inicializado preguiçosamente.

Cache de exceção Quando você usa métodos de fábrica, as exceções são armazenadas em cache. Ou seja, se o método de fábrica gerar uma exceção na primeira vez que um thread tentar acessar a Value propriedade do Lazy<T> objeto, a mesma exceção será gerada em todas as tentativas subsequentes. Isso garante que cada chamada à Value propriedade produza o mesmo resultado e evite erros sutis que possam surgir se threads diferentes obtiverem resultados diferentes. O Lazy<T> valor significa um real T que, caso contrário, teria sido inicializado em algum momento anterior, geralmente durante a inicialização. Uma falha nesse ponto anterior geralmente é fatal. Se houver um potencial para uma falha recuperável, recomendamos que você crie a lógica de repetição na rotina de inicialização (nesse caso, o método de fábrica), assim como faria se não estivesse usando inicialização lenta.

Alternativa ao bloqueio Em determinadas situações, talvez você queira evitar a sobrecarga do comportamento de Lazy<T> bloqueio padrão do objeto. Em situações raras, pode haver um potencial para deadlocks. Nesses casos, você pode usar o construtor ou o Lazy<T>(LazyThreadSafetyMode) construtor Lazy<T>(Func<T>, LazyThreadSafetyMode) e especificar LazyThreadSafetyMode.PublicationOnly. Isso permite que o Lazy<T> objeto crie uma cópia do objeto inicializado preguiçosamente em cada um dos vários threads se os threads chamarem a Value propriedade simultaneamente. O Lazy<T> objeto garante que todos os threads usem a mesma instância do objeto inicializado preguiçosamente e descarta as instâncias que não são usadas. Assim, o custo de reduzir a sobrecarga de bloqueio é que o programa às vezes pode criar e descartar cópias extras de um objeto caro. Na maioria dos casos, isso é improvável. Os exemplos para os construtores e os Lazy<T>(LazyThreadSafetyMode) construtores Lazy<T>(Func<T>, LazyThreadSafetyMode) demonstram esse comportamento.

Importante

Quando você especifica LazyThreadSafetyMode.PublicationOnly, as exceções nunca são armazenadas em cache, mesmo que você especifique um método de fábrica.

Construtores equivalentes Além de habilitar o uso de LazyThreadSafetyMode.PublicationOnly, os construtores e Lazy<T>(Func<T>, LazyThreadSafetyMode) construtores Lazy<T>(LazyThreadSafetyMode) podem duplicar a funcionalidade dos outros construtores. A tabela a seguir mostra os valores de parâmetro que produzem comportamento equivalente.

Para criar um Lazy<T> objeto que seja Para construtores que têm um LazyThreadSafetyMode mode parâmetro, defina mode como Para construtores que têm um parâmetro booliano isThreadSafe , defina isThreadSafe como Para construtores sem parâmetros de segurança de thread
Totalmente thread safe; usa o bloqueio para garantir que apenas um thread inicialize o valor. ExecutionAndPublication true Todos esses construtores são totalmente thread safe.
Não thread safe. None false Não aplicável.
Totalmente thread safe; threads correm para inicializar o valor. PublicationOnly Não aplicável. Não aplicável.

Outros recursos Para obter informações sobre o uso com Lazy<T> campos estáticos de thread ou como repositório de backup para propriedades, consulte Inicialização Lenta.

Construtores

Lazy<T>()

Inicializa uma nova instância da classe Lazy<T>. Quando ocorre a inicialização lenta, o construtor sem parâmetros do tipo de destino é usado.

Lazy<T>(Boolean)

Inicializa uma nova instância da classe Lazy<T>. Quando ocorre a inicialização lenta, o construtor sem parâmetros do tipo de destino e o modo de inicialização especificado são usados.

Lazy<T>(Func<T>)

Inicializa uma nova instância da classe Lazy<T>. Quando ocorre uma inicialização lenta, a função de inicialização especificada é usada.

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

Inicializa uma nova instância da classe Lazy<T>. Quando ocorre a inicialização lenta, a função de inicialização especificada e o modo de inicialização são usados.

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

Inicializa uma nova instância da classe Lazy<T> que usa a função de inicialização especificada e o modo de segurança do thread.

Lazy<T>(LazyThreadSafetyMode)

Inicializa uma nova instância da classe Lazy<T> que usa o construtor sem parâmetros T e o modo de acesso thread-safe especificado.

Lazy<T>(T)

Inicializa uma nova instância da classe Lazy<T> que usa um valor especificado pré-inicializado.

Propriedades

IsValueCreated

Obtém um valor que indica se um valor foi criado para essa instância Lazy<T>.

Value

Obtém o valor de inicialização ociosa da instância Lazy<T> atual.

Métodos

Equals(Object)

Determina se o objeto especificado é igual ao objeto atual.

(Herdado de Object)
GetHashCode()

Serve como a função de hash padrão.

(Herdado de Object)
GetType()

Obtém o Type da instância atual.

(Herdado de Object)
MemberwiseClone()

Cria uma cópia superficial do Object atual.

(Herdado de Object)
ToString()

Cria e retorna uma representação de cadeia de caracteres da propriedade Value para esta instância.

Aplica-se a

Acesso thread-safe

Por padrão, todos os membros públicos e protegidos da Lazy<T> classe são thread safe e podem ser usados simultaneamente de vários threads. Essas garantias de segurança de thread podem ser removidas opcionalmente e por instância, usando parâmetros para os construtores do tipo.

Confira também