C#  - Inicialização Diferida ( Lazy Initialization )


 Neste artigo vou apresentar os conceitos sobre inicialização diferida ou lazy initialization usadas na linguagem C#.
 
Inicialização/Instanciação diferida

A inicialização/instanciação diferida de um objeto significa que a sua criação foi postergada, adiada, diferida até que ele seja usado pela primeira vez.

Mas por que se usa a inicialização diferida ?

Ela é usada principalmente para aumentar o desempenho, evitar desperdício de recursos de computação, reduzir os requisitos de memória da aplicação.

E quando ela é usada ?

1 - Quando você tem um objeto que é muito custoso (em termos de recursos) para criar e ainda pode não ser usado pela aplicação.

Cenário 1 :

Você possui em memória um objeto Cliente que possui uma propriedade Pedidos que contém uma grande quantidade de objetos Pedido que,
para ser inicializado, requer uma conexão com um banco de dados.

Se o usuário nunca solicitar a exibição dos pedidos ou nunca usar os dados para realizar cálculos, não existe motivo para usar memória do
sistema ou recursos para criar os objetos.

Usando a inicialização diferida evitamos gastar recursos do sistema e da aplicação quando o objeto não esta sendo usado.

Ele será criado somente quando for usado, e, se for usado.
 

2 -  Quando você tem um objeto que é custoso para criar, e que você quer adiar a criação até depois que outras operações custosas forem concluídas.

Cenário 2 :

Sua aplicação carrega várias instâncias de objetos quando inicia, mas apenas alguns destes objetos são necessários de imediato.

Você pode melhorar o desempenho da inicialização do programa adiando a instanciação dos objetos que não serão necessários
até que objetos requeridos sejam criados.
 

Como fazer a inicialização diferida ? 

Embora você possa escrever o seu próprio código para realizar a inicialização diferida, é recomendado (pela Microsoft) que você use a classe Lazy<T>.

Essa classe e seus tipos relacionados também suportam a segurança de thread, ou seja, são thread-safety (garante uma execução segura através de várias threads ao mesmo tempo), e assim fornecem uma política de propagação de exceções consistente.

A seguir temos os tipos que a plataforma .NET fornece para habilitar a inicialização diferida em diferentes cenários:

Tipo Descrição
 Lazy<T>  Uma classe invólucro que fornece a semântica da inicialização diferida para qualquer classe ou tipo definido pelo usuário.
 ThreadLocal<T>  Semelhante a Lazy<T>, exceto que fornece a semântica da inicialização lenta em uma base de segmento local. Cada segmento tem acesso ao seu próprio valor único.
 LazyInitializer  Fornece métodos estáticos para a inicialização diferida de objetos sem a sobrecarga de uma classe.

Para definir um tipo com inicialização diferida usamos :  Lazy<Tipo>   ou   Lazy(Of Tipo) (VB .NET)

Exemplo:  Assuma que Pedidos é uma classe que contém um array de objetos Pedido retornados de um banco de dados e um objeto Cliente contém uma instância de Pedidos,mas dependendo da ação do usuário os dados dos objetos Pedidos podem ou não ser requeridos.

  C# VB .NET
Usando o construtor de Lazy<T> para inicializar os objetos. O array Pedidos não será criado agora
Lazy<Pedidos> _pedidos = new Lazy<Pedidos>();
 

Dim _pedidos As Lazy(Of Pedidos) = New Lazy(Of Pedidos)()

Podemos também passar um delegate no construtor Lazy<T> que invoca um construtor específico sobrecarregado no tipo no momento da criação. // A inicialização ocorre pela invocação de
// um construtor especifico no Pedido  quando a propriedade Value for acessada.

Lazy<Pedidos> _pedidos = new Lazy<Pedidos>(() => new Pedidos(100));

Dim
_orders As Lazy(Of Pedidos) = New Lazy(Of Pedidos)(Function() New Pedidos(100))
Depois que o objeto diferido é criado nenhuma instância de Pedidos é criada  até que a propriedade Value da variável Lazy é acessada pela primeira vez.

No primeiro acesso o tipo é criado e retornado e armazenado para acesso futuro.

//precisamos criar os pedidos somente se exibePedido for true
if
(exibePedidos == true) {
    ExibePedidos(_pedidos.Value.PedidoData);
}
else  {
   
// Não gasta recursos para obter os  dados do pedido
}
If exibePedidos = True Then
    ExibePedidos(_pedidos.Value.OrderData)
Else
  
// Não gasta recursos para obter os  dados do pedido
End If

 

Um objeto Lazy<T> sempre retorna o mesmo objeto ou valor com o qual ele foi inicializado. Por isso a propriedade Value é somente leitura. _pedidos = new Lazy<Pedidos>() => new Pedidos(10));
_pedidos = New Lazy(Of Pedidos)(Function() New Pedidos(10))

 

Conclusão:

A inicialização/instanciação diferida é usada para criar um objeto somente na primeira vez que ele for realmente utilizado. Isso atrasa o custo de criar o objeto ate que seja usado.

Geralmente usamos este recurso quando o objeto pode ou não ser usado e o custo de sua construção não é trivial.

"Portanto nós também, pois que estamos rodeados de uma tão grande nuvem de testemunhas, deixemos todo o embaraço, e o pecado que tão de perto nos rodeia, e corramos com paciência a carreira que nos está proposta,"
Hebreus 12:1

Referências:


José Carlos Macoratti