2008-11-18 11 views
86

¿Es mejor para inicializar variables miembro de clase en la declaraciónInicialización de variable de miembro C#; ¿mejores prácticas?

private List<Thing> _things = new List<Thing>(); 
private int _arb = 99; 

o en el constructor por defecto?

private List<Thing> _things; 
private int _arb; 

public TheClass() 
{ 
    _things = new List<Thing>(); 
    _arb = 99; 
} 

¿Es simplemente una cuestión de estilo o hay compensaciones de rendimiento, de una manera u otra?

+2

Posible duplicado de http://stackoverflow.com/questions/24551/best-practice-initialize-class-fields-in-constructor-or-at-declaration – goodeye

Respuesta

70

En términos de rendimiento, no hay diferencia real; los inicializadores de campo se implementan como lógica de constructor. La única diferencia es que los inicializadores de campo suceden antes que cualquier constructor "base"/"este".

El enfoque constructor se puede utilizar con propiedades auto-aplicado (inicializadores de campo pueden no) - es decir

[DefaultValue("")] 
public string Foo {get;set;} 
public Bar() { // ctor 
    Foo = ""; 
} 

Aparte de eso, I tienden a preferir la sintaxis campo inicializador; Me parece que mantiene las cosas localizados - es decir

private readonly List<SomeClass> items = new List<SomeClass>(); 
public List<SomeClass> Items {get {return items;}} 

que no tienen que ir a la caza de arriba a abajo para encontrar donde se le asigna ...

La excepción obvia es donde tiene que realizar la lógica compleja o tratar con parámetros de constructor, en cuyo caso la inicialización basada en el constructor es el camino a seguir. Del mismo modo, si usted tiene varios constructores, sería preferible que los campos para obtener ajuste siempre de la misma manera - por lo que podría tener ctors como:

public Bar() : this("") {} 
public Bar(string foo) {Foo = foo;} 

edición: como comentario al margen, tenga en cuenta que en el anterior, si hay otros campos (no se muestran) con los inicializadores de campo, entonces solo se inicializan directamente en los constructores que llaman al base(...), es decir, el public Bar(string foo) ctor. El otro constructor no hace inicializadores de campo de ejecución, ya que sabe que se hacen por el this(...) ctor.

+0

Sé que esta es una publicación anterior, pero tengo una pregunta: ¿Qué quisiste decir con "constructores que llaman base (. ..) "? tu barra pública (string foo) {Foo = foo;} no parece estar llamando: base(), o sucede implícitamente? Gracias por tu ayuda. –

+1

@Bruno para una 'clase', cada constructor tiene': base() 'implícito a menos que agregue algo más específico, que podría ser': base (123, "abc") ', o podría ser': this (123 , "abc") '. –

+0

@Marc So Is la inicialización en el orden (1) fieldInitializer (2) BaseConstructor (3) LocalConstructor – prabhakaran

1

Por ejemplo, las variables, en gran medida es una cuestión de estilo (prefiero usar un constructor). Para variables estáticas, hay un performance benefit para inicializar en línea (no siempre es posible, por supuesto).

0

Depende de usted.
A menudo los inicializo en línea, porque no me gusta tener un constructor cuando realmente no lo necesito (¡me gustan las clases pequeñas!).

8

En realidad, los inicializadores de campo como lo demuestra son una abreviatura conveniente. El compilador realmente copia el código de inicialización al principio de cada constructor de instancia que defina para su tipo.

Esto tiene dos implicaciones: primero, cualquier código de inicialización de campo está duplicado en cada constructor y, en segundo lugar, cualquier código que incluya en sus constructores para inicializar campos a valores específicos volverá a asignar los campos.

Por lo que respecta al rendimiento, y con respecto al tamaño del código compilado, es mejor que mueva los inicializadores de campo a los constructores.

Por otro lado, el impacto en el rendimiento y el código 'hinchazón' serán generalmente insignificantes, y la sintaxis del inicializador de campo tiene la importante ventaja de disminuir el riesgo de que olvide inicializar algún campo en uno de sus constructores.

+2

El punto de rendimiento solo se aplica si reasigna los valores (es decir, no se aplica en el código original). Del mismo modo, el problema de "abotagamiento" (que es minúsculo) solo se aplica a los ctors que llaman base (...), por lo que puede dejar de lado esto con un ctor privado (tal como se publicó), solo este ctor iniciará los campos. –

+0

¿Sabes si esta reasignación realmente sucede en la práctica? El compilador debería poder eliminar las inicializaciones adicionales, si no cambia la semántica del programa. –

2

Utilice los inicializadores de campo o cree una función Init(). El problema de poner estas cosas en su constructor es que si alguna vez necesita agregar un segundo constructor, termina con el código de copiar/pegar (o lo pasa por alto y termina con variables no inicializadas).

Inicializaría donde estaba declarado. O haga que el constructor (es) llame a una función Init().

+4

Tenga en cuenta el uso de: esto() para encadenar constructores en la publicación anterior de Marc Gravell. Esta es una práctica mucho mejor que escribir funciones Init() separadas. –

3

Una de las principales limitaciones con los inicializadores de campo es que no hay forma de envolverlos en un bloque try-finally. Si se lanza una excepción en un inicializador de campo, se abandonará cualquier recurso que se haya asignado en los inicializadores anteriores; no hay forma de prevenirlo Se pueden resolver otros errores en la construcción, si es torpe, al tener un constructor base protegido que acepte un identificador desechable por referencia, y apuntarlo a sí mismo como su primera operación. Entonces uno puede evitar llamar al constructor excepto a través de métodos de fábrica que en caso de excepción llamarán a Dispose en el objeto parcialmente creado. Esta protección permitirá la limpieza de IDisposables creados en inicializadores de clase derivada si el constructor de la clase principal falla después de "contrabandear" una referencia al nuevo objeto. Lamentablemente, no hay forma de proporcionar dicha protección si falla un inicializador de campo.

0

En el punto agregado a lo anterior: siempre tiene un constructor cuando implementa clases que tienen una implementación. Si no declara uno, el compilador infiere el instructor predeterminado [public Foo() {}]; un constructor que no toma argumentos

Muchas veces me gusta ofrecer ambos enfoques. Permitir constructores para aquellos que deseen usarlos y permitir los Inicializadores de Campo para las situaciones en las que desee utilizar una implementación simplificada o predeterminada de su clase/tipo. Esto agrega flexibilidad a su código. Tenga en cuenta que cualquiera puede usar el inicializador de campo predeterminado si lo desea ... asegúrese de declararlo manualmente si ofrece más de un constructor: público Foo() {}