2009-01-13 19 views
7

A menudo me encuentro escribiendo una propiedad que se evalúa perezosamente. Algo así como:Implementar una clase "LazyProperty": ¿es esta una buena idea?

if (backingField == null) 
    backingField = SomeOperation(); 
return backingField; 

No es mucho código, pero sí se repiten mucho si usted tiene una gran cantidad de propiedades.

estoy pensando en la definición de una clase llamada LazyProperty:

public class LazyProperty<T> 
    { 
    private readonly Func<T> getter; 

    public LazyProperty(Func<T> getter) 
    { 
     this.getter = getter; 
    } 

    private bool loaded = false; 
    private T propertyValue; 

    public T Value 
    { 
     get 
     { 
      if (!loaded) 
      { 
       propertyValue = getter(); 
       loaded = true; 
      } 
      return propertyValue; 
     } 
    } 

    public static implicit operator T(LazyProperty<T> rhs) 
    { 
     return rhs.Value; 
    } 
} 

Esto me permitiría inicializar un campo como este:

first = new LazyProperty<HeavyObject>(() => new HeavyObject { MyProperty = Value }); 

Y entonces el cuerpo de la propiedad podría reducirse a :

public HeavyObject First { get { return first; } } 

Esto sería utilizado por la mayoría de la compañía, ya que entraría en un biblioteca de clases común compartida por la mayoría de nuestros productos.

No puedo decidir si esta es una buena idea o no. Creo que las soluciones tienen algunas ventajas, como:

  • menos código
  • Prettier código

En el lado negativo, sería más difícil de mirar el código y determinar exactamente lo que sucede - especialmente si una el desarrollador no está familiarizado con la clase LazyProperty.

¿Qué opinas? ¿Es esta una buena idea o debería abandonarla? Además, ¿el operador implícito es una buena idea, o preferiría usar la propiedad Value explícitamente si debería usar esta clase?

Las opiniones y sugerencias son bienvenidas :-)

Respuesta

7

Sólo para estar excesivamente pedante:

Su propuesta de solución para evitar la repetición de código:

private LazyProperty<HeavyObject> first = 
    new LazyProperty<HeavyObject>(() => new HeavyObject { MyProperty = Value }); 
public HeavyObject First { 
    get { 
    return first; 
    } 
} 

es en realidad más caracteres que el código que no quería repetir:

private HeavyObject first; 
public HeavyObject First { 
    get { 
    if (first == null) first = new HeavyObject { MyProperty = Value }; 
    return first; 
    } 
} 

Aparte de eso, creo que el reparto implícito hizo que el código sea muy difícil de entender.No hubiera supuesto que un método que simplemente retorna primero, realmente termine creando un HeavyObject. Al menos habría descartado la conversión implícita y habría regresado primero. Valor de la propiedad.

+0

Se señala su punto sobre el operador implícito. Es una de esas cosas sobre las que necesitaba opiniones. – driis

+1

Es bueno ser pedante. +1. – dalle

1

me gusta la idea de que es mucho menos código y más elegante, pero serían muy preocupados por el hecho de que se hace difícil mirarlo y decirle Que esta pasando. La única forma en que lo consideraría es tener una convención para las variables establecidas usando la forma "perezosa", y también para comentar en cualquier lugar donde se use. Ahora no va a haber un compilador ni nada que haga cumplir esas reglas, así que aún YMMV.

Al final, para mí, decisiones como esta se reducen a quién va a mirar y la calidad de esos programadores. Si puedes confiar en tus compañeros desarrolladores para que lo usen bien y comenten bien, entonces hazlo, pero si no, es mejor que lo hagas de una manera fácil de entender y seguir./my 2cents

2

Prefiero el primer código, porque a) es un patrón tan común con las propiedades que inmediatamente lo entiendo, yb) el punto que planteó: que no hay magia oculta que tiene que ir a buscar para comprender dónde y cuándo se obtiene el valor.

1

No creo que preocuparse por un desarrollador que no entiende es un buen argumento en contra de hacer algo como esto ...

Si usted piensa que entonces no se podía hacer nada por el temor de que alguien no entender lo que hizo

Se podría escribir un tutorial o algo en un repositorio central, tenemos aquí un wiki para este tipo de señala

en general, creo que es una buena idea de la aplicación (no querer iniciar un debate sobre si lazy loading es una buena idea o no)

0

me gusta su solución ya que es muy inteligente, pero no te creo gana mucho usándolo. Lazy cargar un campo privado en una propiedad pública es definitivamente un lugar donde se puede duplicar el código. Sin embargo, esto siempre me ha parecido un patrón de uso en lugar de código que debe ser refactorizado en un lugar común.

Su enfoque puede convertirse en una preocupación en el futuro si realiza una serialización. También es más confuso inicialmente entender lo que está haciendo con el tipo personalizado.

En general, aplaudo su intento y aprecio su astucia, pero le sugiero que vuelva a su solución original por las razones indicadas anteriormente.

4

Seguramente al menos desea que el LazyPropery<T> sea un tipo de valor, de lo contrario, habrá agregado memoria y presión de GC para cada propiedad "cargada de forma diferida" en su sistema.

Además, ¿qué pasa con los escenarios de subprocesos múltiples? Considere dos hilos solicitando la propiedad al mismo tiempo. Sin bloqueo, podría crear dos instancias de la propiedad subyacente. Para evitar bloquear el caso común, debería hacer un bloqueo con doble verificación.

+0

+1 para el bloqueo, no es demasiado difícil de trabajar en su diseño, aunque –

+0

Sí, me gustaría además probablemente creado una clase inhertied para implementar el mismo modelo, pero con la seguridad de rosca en el captador de valor. Entonces, el consumidor de la clase podría elegir si quiere/necesita seguridad de hilo o no. – driis

1

Lo que hago en este caso es crear un Visual Studio code snippet. Creo que eso es lo que deberías hacer.

Por ejemplo, al crear controles ASP.NET, que muchas veces tengo datos que se almacenan en el ViewState mucho, así que creé un fragmento de código como esto:

public Type Value 
{ 
    get 
    { 
     if(ViewState["key"] == null) 
      ViewState["key"] = someDefaultValue; 
     return (Type)ViewState["key"]; 
    } 
    set{ ViewState["key"] = value; } 
} 

De esta manera, el código se puede crear fácilmente con solo un poco de trabajo (definiendo el tipo, la clave, el nombre y el valor predeterminado). Es reutilizable, pero no tiene la desventaja de una pieza compleja de código que otros desarrolladores podrían no entender.

0

Personalmente, no creo que la clase LazyProperty como ofrezca el valor suficiente para justificar su uso, especialmente teniendo en cuenta los inconvenientes que tiene para los tipos de valor (como mencionó Kent). Si necesitabas otra funcionalidad (como hacerla multiproceso), podría justificarse como una clase ThreadSafeLazyProperty.

En cuanto a la propiedad implícita, me gusta más la propiedad "Valor". Es un poco más tipeo, pero mucho más claro para mí.

5

No lo hagas para nada.

En general el uso de este tipo de propiedades inicializadas perezoso es una opción de diseño válida en un caso: cuando SomeOperation(); es una operación costosa (en términos de E/S, al igual que cuando se requiere un golpe DB o computacionalmente) Y cuando Es cierto que a menudo NO necesitarás acceder a él.

Dicho esto, de forma predeterminada debe buscar una inicialización entusiasta, y cuando el perfilador dice que es su cuello de botella, cámbielo a una inicialización diferida.

Si siente la necesidad de crear ese tipo de abstracción, es un olor.

+0

No pregunta si los campos evaluados vagos son una opción válida (entiende la distinción cuando es y cuando no). Él pregunta si la solución que presenta es una buena elección de diseño. –

+0

Bueno, sí dice que está haciendo muchas cosas flojas, lo cual también me parece extraño. La mayoría de las propiedades no deberían requerir inodos perezosos. –

+1

@petr, sí, sé cuál fue la pregunta. Solo digo que esto es una mala idea y por qué. Advirtiendo que no se corte, es una respuesta válida a la pregunta "¿cómo me corto mejor?" –

0

Creo que esta es una idea interesante. Primero, le recomendaría que oculte la propiedad Lazy del código de llamada. No desea filtrar en su modelo de dominio que es vago. Que estás haciendo con el operador implícito, así que mantenlo así.

Me gusta cómo puede utilizar este enfoque para manejar y abstraer los detalles de bloqueo, por ejemplo. Si haces eso, entonces creo que hay valor y mérito. Si agrega bloqueo de vigilancia para el patrón de doble bloqueo, es muy fácil equivocarse.

Cuestiones relacionadas