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
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 |
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: