Quando devo usar preguiça?

Encontrei este artigo sobre Lazy: preguiça em C # 4.0-preguiçoso

Qual é a melhor prática para ter o melhor desempenho usando objetos preguiçosos? Alguém me pode indicar um uso prático numa aplicação a sério? Por outras palavras, quando devo usá-lo?

Author: Matthew, 2011-07-27

6 answers

Normalmente usa-se quando se quer instanciar algo da primeira vez que é realmente usado. Isso atrasa o custo de criá-lo até se/quando é necessário em vez de sempre incorrer no custo.

Normalmente isto é preferível quando o objecto pode ou não ser utilizado e o custo da sua construção não é trivial.

 192
Author: James Michael Hare, 2011-07-27 16:16:46

Você deve tentar evitar usar Singletons, mas se alguma vez precisar, Lazy<T> torna fácil implementar singletons preguiçosos e seguros:

public sealed class Singleton
{
    // Because Singleton's constructor is private, we must explicitly
    // give the Lazy<Singleton> a delegate for creating the Singleton.
    static readonly Lazy<Singleton> instanceHolder =
        new Lazy<Singleton>(() => new Singleton());

    Singleton()
    {
        // Explicit private constructor to prevent default public constructor.
        ...
    }

    public static Singleton Instance => instanceHolder.Value;
}
 107
Author: Matthew, 2018-04-04 18:17:24

Um grande real-world Exemplo de onde o carregamento preguiçoso vem à mão é com o ORM's (Object Relation Mappers), como o Entity Framework e NHibernate.

Digamos que você tem um cliente de entidade que tem propriedades para Nome, Número de telefone e ordens. Nome e número de telefone são strings regulares, mas ordens é uma propriedade de navegação que retorna uma lista de cada ordem que o cliente já fez.

Muitas vezes você pode querer ver todos os seus clientes e obter o seu nome e telefone número para lhes ligar. Esta é uma tarefa muito rápida e simples, mas imagine se cada vez que você criou um cliente ele automaticamente foi e fez uma junção complexa para devolver milhares de encomendas. A pior parte é que nem sequer vais usar as ordens, por isso é um completo desperdício de recursos!

Este é o lugar perfeito para o carregamento preguiçoso, porque se a propriedade de encomenda for preguiçosa, não irá buscar toda a encomenda do cliente, a menos que você realmente precise deles. Você pode enumerar o cliente objetos recebendo apenas o seu nome e número de telefone, enquanto a propriedade da ordem está pacientemente dormindo, pronto para quando você precisa.
 71
Author: Despertar, 2014-06-17 03:57:12

Tenho pensado em usar Lazy<T> Propriedades para ajudar a melhorar o desempenho do meu próprio código (e aprender um pouco mais sobre ele). Eu vim aqui à procura de respostas sobre quando usá-lo, mas parece que em todo lugar que eu vou há frases como:

Use a inicialização preguiçosa para adiar a criação de um grande ou objeto de uso intensivo de recursos, ou a execução de um recurso intensivo tarefa, particularmente quando tal criação ou execução pode não ocorrer durante a vida do programa.

De MSDN Lazy Class

Fiquei um pouco confuso porque não sei onde traçar o limite. Por exemplo, eu considero a interpolação linear como uma computação bastante rápida, mas se eu não preciso fazer isso, então pode a inicialização preguiçosa me ajudar a evitar fazê-lo e vale a pena?

No final decidi experimentar o meu próprio teste e pensei em partilhar os resultados aqui. Infelizmente, não sou especialista em fazer este tipo de coisas. por isso, tenho todo o gosto em receber comentários que sugiram melhorias.

Designação das mercadorias

Para o meu caso, eu estava particularmente interessado em ver se Propriedades preguiçosas poderiam ajudar a melhorar uma parte do meu código que faz muita interpolação (a maior parte sendo não utilizada) e por isso eu criei um teste que comparou 3 abordagens.

Criei uma classe de ensaio separada com 20 propriedades de ensaio (vamos chamá-las de propriedades-t) para cada aproximação.

  • GetInterp Classe: executa a interpolação linear sempre que uma propriedade-t é obtida.
  • Classe InitInterp: inicia as propriedades t executando a interpolação linear para cada uma delas no construtor. O get só devolve um duplo.
  • a classe InitLazy: configura as propriedades t como propriedades preguiçosas para que a interpolação linear seja executada uma vez quando a propriedade é obtida pela primeira vez. O subseqüente gets deve apenas devolver um dobro já calculado.

O teste os resultados são medidos em ms e são a média de 50 instanciações ou 20 propriedades recebe. Cada teste foi então executado 5 vezes.

Resultados do ensaio 1: instanciação (média de 50 instanciações)

Class      1        2        3        4        5        Avg       %
------------------------------------------------------------------------
GetInterp  0.005668 0.005722 0.006704 0.006652 0.005572 0.0060636 6.72
InitInterp 0.08481  0.084908 0.099328 0.098626 0.083774 0.0902892 100.00
InitLazy   0.058436 0.05891  0.068046 0.068108 0.060648 0.0628296 69.59

Teste 2 resultados: {[31] } primeiro Get (média de 20 propriedades gets)

Class      1        2        3        4        5        Avg       %
------------------------------------------------------------------------
GetInterp  0.263    0.268725 0.31373  0.263745 0.279675 0.277775 54.38
InitInterp 0.16316  0.161845 0.18675  0.163535 0.173625 0.169783 33.24
InitLazy   0.46932  0.55299  0.54726  0.47878  0.505635 0.510797 100.00

Teste 3 resultados: segunda obtenção (média de 20 aquisições de propriedades)

Class      1        2        3        4        5        Avg       %
------------------------------------------------------------------------
GetInterp  0.08184  0.129325 0.112035 0.097575 0.098695 0.103894 85.30
InitInterp 0.102755 0.128865 0.111335 0.10137  0.106045 0.110074 90.37
InitLazy   0.19603  0.105715 0.107975 0.10034  0.098935 0.121799 100.00

Observações

GetInterp é o mais rápido a entrar como esperado porque não está a fazer nada. InitLazy é mais rápido a instanciar do que InitInterp sugerindo que a sobrecarga na configuração de propriedades preguiçosas é mais rápida do que o meu cálculo de interpolação linear. No entanto, estou um pouco confuso aqui, porque InitInterp deve estar fazendo 20 interpolações lineares (para configurar é t-propriedades), mas é só tirar a 0,09 ms para instanciar (teste 1), em comparação com GetInterp que leva 0.28 ms para fazer apenas uma interpolação linear a primeira vez (teste 2), e 0.1 ms para o fazer pela segunda vez (teste 3).

É preciso InitLazy quase 2 vezes mais do que GetInterp para obter uma propriedade da primeira vez, enquanto InitInterp é o mais rápido, porque ele povoou suas propriedades durante a instanciação. (Pelo menos é o que deveria ter feito, mas por que foi o resultado da instanciação muito mais rápido do que uma única interpolação linear? Quando é que está exactamente a fazer estas interpolações?)

Infelizmente, parece que há uma optimização automática do Código. nos meus testes. Deve levar GetInterp o mesmo tempo para obter uma propriedade da primeira vez como faz da segunda vez, mas está mostrando como mais de 2x mais rápido. Parece que esta otimização também está afetando as outras classes, uma vez que todos eles estão tomando a mesma quantidade de tempo para o teste 3. No entanto, essas optimizações também podem ter lugar no meu próprio código de produção, o que também pode ser uma consideração importante.

Conclusões

Embora alguns resultados sejam espera-se que também haja alguns resultados inesperados muito interessantes, provavelmente devido a otimizações de código. Mesmo para as classes que parecem estar fazendo muito trabalho no construtor, os resultados da instanciação mostram que eles ainda podem ser muito rápidos de criar, em comparação com a obtenção de uma propriedade dupla. Enquanto especialistas neste campo podem ser capazes de comentar e investigar mais profundamente, minha sensação pessoal é que eu preciso fazer este teste novamente, mas no meu código de produção, a fim de examinar que tipo de as optimizações também podem ter lugar nesse país. No entanto, estou à espera que InitInterp possa ser o caminho a seguir.

 36
Author: Ben, 2014-02-28 15:27:55

Só para apontar para o exemplo publicado por Mathew

public sealed class Singleton
{
    // Because Singleton's constructor is private, we must explicitly
    // give the Lazy<Singleton> a delegate for creating the Singleton.
    private static readonly Lazy<Singleton> instanceHolder =
        new Lazy<Singleton>(() => new Singleton());

    private Singleton()
    {
        ...
    }

    public static Singleton Instance
    {
        get { return instanceHolder.Value; }
    }
}
Antes de os preguiçosos nascerem, teríamos feito assim.
private static object lockingObject = new object();
public static LazySample InstanceCreation()
{
    if(lazilyInitObject == null)
    {
         lock (lockingObject)
         {
              if(lazilyInitObject == null)
              {
                   lazilyInitObject = new LazySample ();
              }
         }
    }
    return lazilyInitObject ;
}
 13
Author: Thulani Chivandikwa, 2017-06-10 20:09:14

De MSDN:

Use uma instância de preguiça para adiar a criação de um objeto grande ou de recurso intensivo ou a execução de uma tarefa de recurso intensivo, particularmente quando tal criação ou execução pode não ocorrer durante a vida do programa.

Além da resposta de James Michael Hare, Lazy fornece inicialização segura de thread do seu valor. Dê uma olhada em LazyThreadSafetyMode enumeration MSDN entry describing various types of thread modos de segurança para esta classe.
 11
Author: Vasea, 2015-10-16 10:03:54