Lazy<T> Classe
Definição
Importante
Algumas informações dizem respeito a um produto pré-lançado que pode ser substancialmente modificado antes de ser lançado. A Microsoft não faz garantias, de forma expressa ou implícita, em relação à informação aqui apresentada.
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 |
| 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.