Lazy<T> Classe

Definição

Oferece suporte para inicialização preguiçosa.

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 Genérico

T

O tipo de objeto que está a ser inicializado de forma preguiçosa.

Herança
Lazy<T>
Derivado
Atributos

Exemplos

O exemplo seguinte demonstra o uso da Lazy<T> classe para fornecer inicialização preguiçosa com acesso a partir de múltiplos threads.

Note

O exemplo usa o Lazy<T>(Func<T>) construtor. Demonstra também 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 mudar para outro construtor, basta mudar quais os construtores que são comentados.

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

O exemplo define uma LargeObject classe que será inicializada preguiçosamente por uma de várias threads. As quatro secçõ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 apresenta uma mensagem quando o objeto é criado. No início do Main método, o exemplo cria o inicializador preguiçoso seguro para threads 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 marcador de posição para uma 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

Note-se que as duas primeiras secções do código podiam ser combinadas usando uma função lambda, como 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 faz uma pausa, para indicar que pode decorrer um período indeterminado antes de ocorrer uma inicialização preguiçosa. Quando carregas na 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 isto acontece, a LargeObject instância é 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 secção chave do código, apresenta uma mensagem e regista a identidade do thread inicializante. A saída do programa aparece no final da lista completa de códigos.

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

Note

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

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
'

Observações

Utilize uma inicialização preguiçosa para adiar a criação de um objeto grande ou que consome muitos recursos, ou a execução de uma tarefa intensiva em recursos, especialmente quando tal criação ou execução pode não ocorrer durante a vida útil do programa.

Para se preparar para uma inicialização preguiçosa, cria-se uma instância de Lazy<T>. O argumento de tipo do Lazy<T> objeto que crias especifica o tipo do objeto que queres inicializar de forma preguiçosa. O construtor que 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, a escolha de um construtor depende das suas respostas a duas perguntas:

  • O objeto inicializado de forma preguiçosa será acedido a partir de mais do que um thread? Se sim, o Lazy<T> objeto pode criá-lo em qualquer thread. Pode usar um dos construtores simples cujo comportamento padrão é criar um objeto seguro Lazy<T> para threads, de modo a que apenas uma instância do objeto instanciado preguiçosamente seja criada, independentemente de quantos threads tentem aceder a ele. Para criar um Lazy<T> objeto que não seja seguro para threads, deve usar um construtor que permita especificar a segurança sem threads.

    Atenção

    Tornar o Lazy<T> objeto thread seguro não protege o objeto inicializado de forma preguiçosa. Se múltiplas threads conseguirem aceder ao objeto inicializado de forma preguiçosa, deve tornar as suas propriedades e métodos seguros para acesso multithread.

  • A inicialização preguiçosa requer muito código, ou o objeto inicializado preguiçosamente tem um construtor sem parâmetros que faz tudo o que precisas e não lança exceções? Se precisares de escrever código de inicialização ou se precisares de tratar exceções, usa um dos construtores que utiliza um método de fábrica. Escreve o teu código de inicialização no método de fábrica.

A tabela seguinte mostra qual construtor escolher, com base nestes dois fatores:

O objeto será acedido por Se não for necessário código de inicialização (construtor sem parâmetros), use Se for necessário código de inicialização, use
Múltiplos threads Lazy<T>() Lazy<T>(Func<T>)
Um fio Lazy<T>(Boolean) com isThreadSafe definido como false. Lazy<T>(Func<T>, Boolean) com isThreadSafe definido como false.

Pode usar uma expressão lambda para especificar o método de fábrica. Isto mantém todo o código de inicialização num só lugar. A expressão lambda capta o contexto, incluindo quaisquer argumentos que passes ao construtor do objeto inicializado de forma preguiçosa.

Cache de exceções Quando usas métodos de fábrica, as exceções são armazenadas em cache. Ou seja, se o método de fábrica lançar uma exceção na primeira vez que um thread tenta aceder à Value propriedade do Lazy<T> objeto, a mesma exceção é lançada em todas as tentativas subsequentes. Isto garante que cada chamada à Value propriedade produz o mesmo resultado e evita erros subtis que possam surgir se diferentes threads obtiverem resultados diferentes. Substitui Lazy<T> um real T que, de outra forma, teria sido inicializado em algum momento anterior, normalmente durante o arranque. Uma falha nesse ponto inicial é geralmente fatal. Se houver potencial para uma falha recuperável, recomendamos que integre a lógica de retentativa na rotina de inicialização (neste caso, o método de fábrica), tal como faria se não estivesse a usar uma inicialização preguiçosa.

Alternativa ao bloqueio Em certas situações, pode querer evitar a sobrecarga do Lazy<T> comportamento de bloqueio padrão do objeto. Em situações raras, pode haver potencial para impasses. Nesses casos, pode usar o Lazy<T>(LazyThreadSafetyMode) construtor ou Lazy<T>(Func<T>, LazyThreadSafetyMode) e especificar LazyThreadSafetyMode.PublicationOnly. Isto 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 todas as threads usam 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 seu programa pode, por vezes, criar e descartar cópias extra de um objeto caro. Na maioria dos casos, isto é improvável. Os exemplos para os Lazy<T>(LazyThreadSafetyMode) construtores e Lazy<T>(Func<T>, LazyThreadSafetyMode) demonstram este comportamento.

Importante

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

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

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

Outras capacidades Para informações sobre o uso de Lazy<T> com campos estáticos de thread, ou como armazenamento de suporte para propriedades, veja Inicialização Preguiçosa.

Construtores

Name Descrição
Lazy<T>()

Inicializa uma nova instância da Lazy<T> classe. Quando ocorre uma inicialização preguiçosa, é utilizado o construtor sem parâmetros do tipo alvo.

Lazy<T>(Boolean)

Inicializa uma nova instância da Lazy<T> classe. Quando ocorre uma inicialização preguiçosa, utilizam-se o construtor sem parâmetros do tipo alvo e o modo de inicialização especificado.

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

Inicializa uma nova instância da Lazy<T> classe. Quando ocorre uma inicialização preguiçosa, são usadas a função de inicialização e o modo de inicialização especificados.

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

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

Lazy<T>(Func<T>)

Inicializa uma nova instância da Lazy<T> classe. Quando ocorre uma inicialização preguiçosa, utiliza-se a função de inicialização especificada.

Lazy<T>(LazyThreadSafetyMode)

Inicializa uma nova instância da Lazy<T> classe que utiliza o construtor sem parâmetros de T e o modo de segurança de thread especificado.

Lazy<T>(T)

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

Propriedades

Name Descrição
IsValueCreated

Obtém um valor que indica se um valor foi criado para esta Lazy<T> ocorrência.

Value

Obtém o valor inicializado preguiçosamente da instância atual Lazy<T> .

Métodos

Name Descrição
Equals(Object)

Determina se o objeto especificado é igual ao objeto atual.

(Herdado de Object)
GetHashCode()

Serve como função de hash predefinida.

(Herdado de Object)
GetType()

Obtém o Type da instância atual.

(Herdado de Object)
MemberwiseClone()

Cria uma cópia superficial do atual Object.

(Herdado de Object)
ToString()

Cria e devolve uma representação string da Value propriedade para esta instância.

Aplica-se a

Segurança de Thread

Por defeito, todos os membros públicos e protegidos da Lazy<T> classe são seguros contra threads e podem ser usados simultaneamente a partir de múltiplas threads. Estas garantias de segurança de thread podem ser removidas opcionalmente e por instância, usando parâmetros dos construtores do tipo.

Ver também