2011-02-27 14 views
47

En .NET 4, el siguiente fragmento con una propiedad en caché también se puede escribir utilizando la clase System.Lazy<T>. Medí el rendimiento de ambos enfoques y es prácticamente lo mismo. ¿Hay algún beneficio real o mágico de por qué debería usar uno sobre el otro?Propiedad en caché vs Lazy <T>

caché de la propiedad

public static class Brushes 
{ 
    private static LinearGradientBrush _myBrush; 

    public static LinearGradientBrush MyBrush 
    { 
     get 
     { 
      if (_myBrush == null) 
      { 
       var linearGradientBrush = new LinearGradientBrush { ...}; 
       linearGradientBrush.GradientStops.Add(...); 
       linearGradientBrush.GradientStops.Add(...); 

       _myBrush = linearGradientBrush; 
      } 

      return _myBrush; 
     } 
    } 
} 

Lazy <T>

public static class Brushes 
{ 
    private static readonly Lazy<LinearGradientBrush> _myBrush = 
     new Lazy<LinearGradientBrush>(() => 
      { 
       var linearGradientBrush = new LinearGradientBrush { ...}; 
       linearGradientBrush.GradientStops.Add(...); 
       linearGradientBrush.GradientStops.Add(...); 

       return linearGradientBrush; 
      } 
     ); 

    public static LinearGradientBrush MyBrush 
    { 
     get { return _myBrush.Value; } 
    } 
} 
+13

Mediante el uso de '' Lazy que estás siendo 'Lazy' para escribir su propia implementación. (En el buen sentido, por supuesto.) – BoltClock

+0

Interesante, me inclinaría a decir que es menos código y más legible, pero su ejemplo demuestra que esto no es exactamente así. Pero, de nuevo, ya tengo una clase 'Propiedad ' para los campos de respaldo que admite este y el comportamiento más común del campo de respaldo. –

+0

Lazy permite hilo de seguridad – Tsayper

Respuesta

68

me gustaría utilizar Lazy<T> en general:

  • de Es seguro para subprocesos (puede no ser un problema en este caso, pero sería en otros)
  • Se hace obvio lo que está pasando justo por el nombrar
  • permite nula a ser un valor válido

importante hacer notar que no tienen utilizar una expresión lambda para el delegado. Por ejemplo, aquí es un enfoque que puede ser un poco más limpio:

public static class Brushes 
{ 
    private static readonly Lazy<LinearGradientBrush> _myBrush = 
     new Lazy<LinearGradientBrush>(CreateMyBrush); 

    private static LinearGradientBrush CreateMyBrush() 
    { 
     var linearGradientBrush = new LinearGradientBrush { ...}; 
     linearGradientBrush.GradientStops.Add(...); 
     linearGradientBrush.GradientStops.Add(...); 

     return linearGradientBrush; 
    } 

    public static LinearGradientBrush MyBrush 
    { 
     get { return _myBrush.Value; } 
    } 
} 

Esto es particularmente útil cuando el proceso de creación se complica con bucles, etc. Observe que por lo visto, se puede utilizar un inicializador de colección para GradientStops en tu código de creación

Otra opción es no para hacer esto perezosamente, por supuesto ... a menos que tenga varias propiedades de ese tipo en su clase y solo quiera crear los objetos relevantes uno a uno, puede confiar en la inicialización de clases perezosas para muchas situaciones.

Como se señala en la respuesta de DoubleDown, no hay forma de restablecer esto para forzar el recálculo (a menos que haga que el campo Lazy<T> no sea de solo lectura), pero rara vez considero que sea importante.

+1

¿En qué caso se utiliza Lazy un problema? – user256034

+14

@ user256034: cuando no estás usando .NET 4 :) –

+0

¡Gracias! Los 3 puntos en su lista son exactamente lo que estaba buscando;) –

1

El Lazy<T> — es más simple que expresa claramente la intención del código.
También es seguro para subprocesos.

Tenga en cuenta que si realmente está utilizando esto en varios hilos, necesita hacerlo [ThreadStatic]; Los objetos GDI + no pueden compartirse entre subprocesos.

2

Lazy<T> manejarán correctamente los escenarios concurrentes (si pasa el LazyThreadSafetyMode correcto) mientras su ejemplo no tiene ninguna verificación de seguridad de hilos.

7

Utilice Lazy<T>, ya que expresa exactamente lo que está haciendo - lazy loading.

Además, mantiene su propiedad muy limpia y es segura para subprocesos.

-1

Bueno, si su rendimiento es aproximadamente el mismo, la única razón para usar Lazy<T> en la versión en caché sería si no está seguro de si el usuario realmente va a cargar la propiedad.

El punto de Lazy<T> es esperar hasta que el usuario necesita el recurso y cree que en ese caso en el tiempo. Si siempre van a necesitar recursos, entonces no tiene sentido usar Lazy<T>, a menos que necesite algunos de sus otros propósitos, como que es seguro para subprocesos.

+1

-1, la alternativa de OP es idéntica en el sentido de que si nunca se llama a la propiedad, entonces la lista nunca se crea una instancia. En ese sentido, ambos son "perezosos". – tsemer

4

Normalmente, la única razón para no usar perezoso es para restablecer la variable a null por lo que el siguiente acceso hace que se cargue de nuevo. Lazy no tiene reinicio y tendrías que recrear el perezoso desde cero.

+0

Ver esta respuesta http://stackoverflow.com/questions/5961252/reset-system-lazy/6255398#6255398 – Olmo

+0

Buen punto! En mi humilde opinión, esto en combinación con la respuesta de @ JonSkeet es la respuesta completa. – tsemer

0

Lazy tiene cierta sobrecarga sincronización para proporcionar hilos de seguridad, mientras que la propiedad caché se initiliazed a modo CLR antes de cualquier otro código y no es necesario pagar synronization costó

Desde el punto de vista de la capacidad de prueba, Lazy está bien probada y artefacto probado.

Sin embargo, tiene una muy ligera sobrecarga, en mi opinión, sobre otra opción