2009-11-24 28 views
9

me parece estar utilizando este tipo de patrón en mi código mucho, sé que no es un Autoproperty sencilla más, ya que eso sería:C# Auto Property - ¿Es esta la mejor práctica de "patrón"?

public IList<BCSFilter> BCSFilters { get; set; } 

El código que he estado usando es la siguiente:

private IList<BCSFilter> _BCSFilters; 

    /// <summary> 
    /// Gets or sets the BCS filters. 
    /// </summary> 
    /// <value>The BCS filters.</value> 
    public IList<BCSFilter> BCSFilters 
    { 
     get 
     { 
      if (_BCSFilters == null) 
      { 
       _BCSFilters = new List<BCSFilter>(); 
      } 

      return _BCSFilters; 
     } 
     set 
     { 
      _BCSFilters = value; 
     } 
    } 

Esto es así, solo puedo hacer MainClass.BCSFilters y no preocuparme por la necesidad de crear una instancia de la Lista en el código de consumo. ¿Es este un patrón "normal" la forma correcta de hacer esto?

no pude encontrar una pregunta duplicado

Respuesta

31

Ésta es una técnica que yo uso mucho a mí mismo. Esto también puede ayudar a ahorrar recursos de memoria ya que no crea una instancia del objeto List <> a menos que la propiedad de los objetos se esté usando realmente dentro del código de consumo. Esto utiliza una técnica de "carga diferida".

Además, la técnica de "Carga diferida" que ha enumerado no es Thread Safe. Si sucede que hay varias llamadas simultáneamente a la propiedad, podría terminar teniendo múltiples llamadas estableciendo la propiedad en un nuevo objeto List <>, sobrescribiendo en consecuencia cualquier valor existente de la Lista con un nuevo objeto List <> vacío. Para hacer que el acceso Get hilo de seguridad es necesario utilizar el Lock statement, así:

private IList<BCSFilter> _BCSFilters; 

// Create out "key" to use for locking 
private object _BCSFiltersLOCK = new Object(); 

/// <summary> 
/// Gets or sets the BCS filters. 
/// </summary> 
/// <value>The BCS filters.</value> 
public IList<BCSFilter> BCSFilters 
{ 
    get 
    { 
     if (_BCSFilters == null) 
     { 
      // Lock the object before modifying it, so other 
      // simultaneous calls don't step on each other 
      lock(_BCSFiltersLOCK) 
      { 
       if (_BCSFilters == null) 
       } 
        _BCSFilters = new List<BCSFilter>(); 
       } 
      } 
     } 

     return _BCSFilters; 
    } 
    set 
    { 
     _BCSFilters = value; 
    } 
} 

Sin embargo, si siempre se necesitará la Lista <> instancia de objeto que es un poco más fácil de simplemente crearlo dentro del constructor de objetos y use la propiedad automática en su lugar. Como la siguiente:

public class MyObject 
{ 
    public MyObject() 
    { 
     BCSFilters = new List<BCSFilter>(); 
    } 

    public IList<BCSFilter> BCSFilters { get; set; } 
} 

Además, si deja el "set" acceso públicos a continuación, el código de consumir será capaz de establecer la propiedad en nulo que puede romper otro código que consume. Por lo tanto, una buena técnica para evitar que el código de consumo pueda establecer el valor de la propiedad en Nulo es establecer que el acceso del conjunto sea privado. De esta manera:

public IList<BCSFilter> BCSFilters { get; private set; } 

Una técnica relacionada es devolver un <> objeto IEnumerable de la propiedad en su lugar. Esto le permitirá reemplazar el List <> escriba internamente dentro del objeto en cualquier momento y el código de consumo no se verá afectado. Para devolver IEnumerable <> puede simplemente devolver el objeto List simple <> directamente ya que implementa la interfaz IEnumerable <>. Como la siguiente:

public class MyObject 
{ 
    public MyObject() 
    { 
     BCSFilters = new List<BCSFilter>(); 
    } 

    public IEnumerable<BCSFilter> BCSFilters { get; set; } 
} 
+0

+1 de acuerdo. no lo agregue donde no sea necesario. –

+0

Gracias Chris por una respuesta clara y concisa –

+0

El único inconveniente es que esta inicialización no es segura para subprocesos, consulte también la respuesta de Rob Levine. (Sería bueno si incorpora esto en su respuesta.) – peterchen

0

Utilizamos ese patrón en mi lugar de trabajo. Es útil porque evita posibles excepciones de referencia nula en el código de consumo y mantiene el código de consumo más simple.

3

Su enfoque es la versión de inicio flojo de

public class xyz 
{ 
    public xyz() 
    { 
     BCSFilters = new List<BCSFilter>(); 
    } 

    public IList<BCSFilter> BCSFilters { get; set; } 
} 
0

Sí, eso es perfectamente normal ;-)

Tal creación perezosa no es infrecuente, y que tiene sentido. La única advertencia es que tendrás que tener cuidado si haces referencia al campo en absoluto.

Editar: Pero tendré que ir con Chris y los demás: es un patrón (mucho) mejor para usar una propiedad de auto e inicializar la colección en el constructor.

0

Es un patrón correcto. Autoproperties es solo una abreviatura para el escenario más simple, y posiblemente el más común con propiedades, y para algunos escenarios, simplemente no es sensato usarlo. Lo que podría hacer, sin embargo, es crear instancias de BCSFilters en el constructor. De esta forma, podría usar las propiedades automáticas y aún así no tendría que preocuparse por las excepciones de referencia nula.

5

Es el patrón correcto, siempre y cuando lo que quiere decir:

  • Permitir código externo para reemplazar toda la lista (instance.BCSFilters = null)
  • tener la lista creada mágicamente en lectura. Sin embargo, es complicado, ya que permite que el usuario lo configure como nulo (en el conjunto) pero no permite que permanezca nulo (dado que una obtención posterior generará una lista vacía).

También puede querer exponer el IList en modo de solo lectura (con init lazy si así lo desea), por lo que los usuarios solo pueden agregar o quitar elementos sin poder sobrescribir la lista. Si tiene muchas asignaciones, puede haber copia involucrada.

Por lo general sólo tienen un captador a los miembros de mi IList, y que incluso pueden exponer a IEnumerable o devolver una copia en el get (pero que tendría que provid específica Agregar y Quitar métodos, por lo que tu caso es distinto)

+0

+1 - la creación de instancias de una lista es una lástima, pero no hay muchas buenas razones para permitir que el código fuera de su clase reemplace toda la instancia de la lista. –

2
Este

es un ejemplo de Lazy Load pattern. Es un patrón aceptado y perfectamente válido.

Puede usar propiedades automáticas en C# y asignar una instancia a la propiedad en un constructor. El beneficio del patrón de carga diferida es que no inicializa la propiedad a menos que se llame. Esto puede ser útil en situaciones donde la inicialización es costosa.

Tiendo a preferir las propiedades automáticas con la inicialización del constructor porque la sintaxis es más concisa y hay menos tipeo a menos que la inicialización de sea costosa, en cuyo caso Lazy Load funciona bien.

+0

Gracias Dariom por el enlace, pensé que debía haber sido algún tipo de patrón ... ahora sé que fue Lazy Load –

10

Su patrón es un patrón de carga lenta totalmente razonable, pero tenga en cuenta que no es seguro.

Si dos hilos acceder esta propiedad por primera vez muy próximos entre sí, el patrón de prueba nula no impediría la condición en la que un hilo evalúa como null, pero antes que llegue la oportunidad para inicializar la lista, el segundo hilo también evalúa a nulo. En esta situación, ambos inicializarán la lista.

Además, existe la posibilidad de que para cuando la propiedad regrese, un hilo tendrá una copia de la lista, mientras que el segundo tendrá otro.

No es un problema en un entorno de subproceso único, pero definitivamente algo a tener en cuenta.

+0

Gracias Rob, Actualmente no estamos usando multi-threading, pero estoy de acuerdo si podemos pensar en estas cosas como lo más temprano posible salvará el dolor de corazón más tarde en –

5

su información, de una manera algo más concisa de hacer exactamente lo mismo que ya está haciendo podría tener este aspecto:

private IList<BCSFilter> _BCSFilters; 

public IList<BCSFilter> BCSFilters 
{ 
    get 
    { 
     return _BCSFilters ?? (_BCSFilters = new List<BCSFilter>()); 
    } 
    set 
    { 
     _BCSFilters = value; 
    } 
} 
+0

Oh sí. El operador de Coalesce simplifica las cosas considerablemente. +1 –

3

hay otro truco :)

Para utilizar perezoso de .Net 4 .

que he visto esto en el blog de Mark Seemann pienso:

public class Order 
{ 
    public Order() 
    { 
     _customerInitializer = new Lazy<Customer>(() => new Customer()); 
    } 

    // other properties 

    private Lazy<Customer> _customerInitializer; 
    public Customer Customer 
    { 
     get 
     { 
      return _customerInitializer.Value; 
     } 
    } 

    public string PrintLabel() 
    { 
     string result = Customer.CompanyName; // ok to access Customer 
     return result + "\n" + _customerInitializer.Value.Address; // ok to access via .Value 
    } 
} 

Aviso º en "_customerInitializer" nunca podría ser nulo, así que es muy similar usarlo. ¡y también podría ser seguro para subprocesos! el constructor puede obtener una sobrecarga con LazyThreadSafetyMode Enum! http://msdn.microsoft.com/en-us/library/system.threading.lazythreadsafetymode.aspx

Cuestiones relacionadas