2011-01-23 16 views
8

¿Hay algún beneficio para la siguiente manera:instanciación de objetos en el constructor

public class Foo 
{ 
    private Bar bar; 

    public Foo() 
    { 
     bar = new Bar(); 
    } 
} 

en lugar de hacerlo de esta manera:

public class Foo 
{ 
    private Bar bar = new Bar(); 

    public Foo() 
    { 
    } 
} 

Teniendo en cuenta que en la instanciación, la variable miembro privada en cualquier ejemplo será instanciado, no creo que haya una diferencia, pero lo he visto tantas veces que siento curiosidad.

+1

Posible duplicado de: http://stackoverflow.com/questions/4219759/create-an-object-in-the-constructor-or-at-top-of-the-class or: http: // stackoverflow. com/questions/298183/c-member-variable-initialization-best-practice –

+0

Gracias por atrapar a los incautos, Yodan –

Respuesta

19

En el caso exacto que ha dado, no hay ninguna diferencia, pero en general sí existe.

Los inicializadores de variable se ejecutan antes de se llama al constructor de la clase base. Si ese constructor base llama a un método virtual que usa algunas de las variables de instancia, puede ver esa diferencia.

Para los fanáticos de especificaciones, es en la sección 10.11.2 de la C# 4 spec:

Cuando un constructor de instancias no tiene inicializador constructor, o tiene un inicializador constructor de la base con forma (...) , ese constructor lleva a cabo implícitamente las inicializaciones especificadas por los inicializadores de variable de los campos de instancia declarados en su clase. Esto corresponde a una secuencia de asignaciones que se ejecutan inmediatamente después de la entrada al constructor y antes de la invocación implícita del constructor de la clase base directa.

He aquí un ejemplo que demuestra esto:

using System; 

public class Base 
{ 
    public Base() 
    { 
     Dump(); 
    } 

    public virtual void Dump() {}  
} 

class Child : Base 
{ 
    private string x = "initialized in declaration"; 
    private string y; 

    public Child() 
    { 
     y = "initialized in constructor"; 
    } 

    public override void Dump() 
    { 
     Console.WriteLine("x={0}; y={1}", x, y); 
    } 
} 

class Test 
{ 
    static void Main(string[] args) 
    { 
     new Child(); 
    } 
} 

Resultado:

x = inicializado en la declaración; y =

Ahora que he dicho lo anterior, me esforzaría por evitar llamar métodos virtuales desde un constructor. Básicamente le estás pidiendo a la clase derivada que funcione de manera parcialmente inicializada. Sin embargo, es una diferencia que debes tener en cuenta.

como donde para inicializar variables ... Tengo que admitir que no soy particularmente consistente, y no creo que eso sea realmente un problema. Si tengo un sesgo específico, es probable que inicie cualquier cosa que no dependa de ningún parámetro en el punto de declaración, dejando que las variables que I no puedan inicializarse sin información adicional para el constructor.

+1

Bah. Shoulda sabía que sonarías mientras estaba ocupado compilando, desensamblando y explicando. :) – cHao

+0

@cHao: No hay necesidad de compilar y desmontar - consultar las especificaciones :) –

+0

@Jon: Eh. Me molestan las personas que citan los estándares como si fueran la única forma en que las cosas pueden ser (tm). Por supuesto en este caso, tiene más sentido; Quiero decir, la gente que lo escribió, también escribió One True Implementation. Creo que he estado dando vueltas por la gente de C/C++ demasiado tiempo. :) – cHao

5

En su caso, no hay una diferencia real en la funcionalidad. Sin embargo, está el problema de averiguar dónde y cómo se inicializa todo. Si pongo la inicialización en el constructor, tengo dos grandes beneficios:

  1. Todo está inicializado en un solo lugar; No tengo que ir a buscar si y dónde se establece.

  2. Si quiero, puedo pasar argumentos al constructor de Bar en función de cómo estoy configurando cosas. Si el inicializador está fuera del constructor, estoy mucho más limitado en cómo puedo inicializar cosas.

Francamente, el IDE ayuda un poco con # 1 ... a pesar de que me dé un tirón lejos del código que estaba mirando, mis clases rara vez son tan grandes como para hacer cosas re-hallazgo un problema . Entonces, si no necesito el n. ° 2, es probable que lo haga de cualquier manera dependiendo del proyecto y mi estado de ánimo. Sin embargo, para proyectos más grandes, me gustaría tener todo mi código de inicio en un solo lugar.

Editar:

bien, al parecer, hay una diferencia entre los dos. Pero el caso donde importa es raro.

he creado las siguientes clases:

class Program 
{ 
    public Program() { Console.WriteLine(this); } 

    static void Main(string[] args) 
    { 
     other p = new other(); 
     Console.WriteLine(p); 
     Console.ReadLine(); 
    } 
} 

class other : Program 
{ 
    string s1 = "Hello"; 
    string s2; 

    public other() { s2 = "World"; } 
    public override string ToString() { return s1 + s2; } 
} 

Ahora, lo que me encontré fue un poco sorprendente e inesperado (si usted no ha leído la especificación C#).

Esto es lo que el constructor de la clase other compilado a:.

.method public hidebysig specialname rtspecialname 
     instance void .ctor() cil managed 
{ 
    // Code size  29 (0x1d) 
    .maxstack 8 
    IL_0000: ldarg.0 
    IL_0001: ldstr  "Hello" 
    IL_0006: stfld  string ConsoleApplication1.other::s1 
    IL_000b: ldarg.0 
    IL_000c: call  instance void ConsoleApplication1.Program::.ctor() 
    IL_0011: ldarg.0 
    IL_0012: ldstr  "World" 
    IL_0017: stfld  string ConsoleApplication1.other::s2 
    IL_001c: ret 
} // end of method other::.ctor 

Nota de la llamada a programar :: Héctor (el constructor de la clase base) intercalado entre los dos ldstr/stfld pares (esos son los que establecer s1 y s2). Es decir, cuando se está ejecutando el constructor base, s2 aún no se ha establecido.

El programa, para referencia, las salidas de los siguientes:

Hello 
HelloWorld 

porque en el constructor del Programa, Console.WriteLine(obj) llamados obj.ToString(), que (ya que el objeto es ya un other) fue other::ToString(). Como el s2 no se configuró aún, no obtuvimos un "Mundo" la primera vez. Si estuviéramos haciendo algo más propenso a errores que simplemente imprimir cosas, esto podría causar problemas reales.

Ahora, este es un ejemplo feo, diseñado para ser un caso patológico. Pero es un excelente argumento en contra de llamar funciones virtuales en su constructor. Sin hacer exactamente eso, esta ruptura no hubiera sido posible. Esa es la única vez que realmente tiene que preocuparse por la diferencia: cuando el constructor de su clase base llama a los métodos virtuales que ha anulado, que se basan en los valores de los campos que se establecen en el constructor. Una ventana bastante estrecha de rompimiento, pero sí.

+0

Con respecto al # 2, por supuesto; es un ejemplo artificial Gracias por la nota sobre la limpieza del código.Estoy de acuerdo en que es más limpio, pero no estaba seguro de si el compilador en el ejemplo artificial trataría a cada uno de una manera diferente. (No suena como eso.) –

1

La diferencia es en el primer caso que tiene la propiedad de la barra inicializada después de que se llama al constructor y en el segundo antes de llamar al constructor. No usas métodos estáticos así que no hay diferencia.

Un poco fuera de tema, pero es mejor manera para inicializar la barra fuera del objeto de esta manera:

public class Foo 
{ 
    private Bar bar; 

    public Foo(Bar bar) 
    { 
     this.bar = bar; 
    } 
} 

De esta manera no son la conexión entre los objetos.

Cuestiones relacionadas