2011-01-28 14 views

Respuesta

23

Un campo con un inicializador se inicializa antes de se llama el constructor base, mientras que si el inicializador está en el cuerpo, que sólo es ejecutado después de el constructor base es llamado.

Esto puede ser relevante si el constructor base llama a un método virtual, pero personalmente trataría de evitar esa situación.

Código de ejemplo:

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

    public virtual void Dump() {} 
} 

public class Child : Base 
{ 
    private string x = "Initialized at declaration"; 
    private string y; 

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

    public override void Dump() 
    { 
     Console.WriteLine(x); // Prints "Initialized at declaration" 
     Console.WriteLine(y); // Prints "" as y is still null 
    } 
} 
+0

+1 Además, mi opinión es que la inicialización de un campo en la declaración permite al compilador/a JITer hacer algunas optimizaciones que de otro modo no haría para una inicialización en el constructor. Creo que * lo leí en las "Pautas de diseño de .NET Framework" como su justificación para preferir la inicialización de campo como una "mejor práctica", pero ya no estoy seguro. –

+0

@Cody: No estoy seguro, para ser sincero. Yo personalmente no tomaría esto en cuenta al escribir el código; me limitaría a seguir la forma más clara de expresar intenciones. –

+0

@Jon Error de Skeet: Clase infantil no heredada de Base en el código – Adeel

1

Es importante tener en cuenta que (en C#) la asignación de inicialización al campo se producirá antes de la llamada a cualquier constructor de la clase base (como se evidencia en this question sobre si VB podría verse obligado para hacer lo mismo).

Esto significa que no puede utilizar la sintaxis de inicializador hacer referencia a un campo de su clase base (es decir, no se puede traducir directamente en este VB C#):

Public Class Base 
    Protected I As Int32 = 4 
End Class 

Public Class Class2 
    Inherits Base 

    Public J As Int32 = I * 10 
End Class 
+0

Jaja, estaba a punto de publicarlo como un comentario a la respuesta de Jon Skeet. –

+0

-1 Eso no es verdad, mira mi publicación –

+0

@Artur: ¿De dónde sacas abajo votando esto sin leerlo primero? Su pequeña prueba compiló el código en ** C# **. Aquí la respuesta de Damien así como [mi pregunta] (http://stackoverflow.com/questions/4602468/can-vb-net-be-forced-to-initialize-instance-variables-before-invoking-the-base-ty) que él vincula para señalar explícitamente que el comportamiento es ** diferente en VB.NET **. –

2

También puede utilizar un constructor estático que obtiene la llamada antes de que cualquier otro constructor de donde se puede init variables estáticas

public class Bus 
{ 
    private static object m_object= null; 

    // Static constructor: 
    static Bus() 
    { 
     m_object = new object(); 

     System.Console.WriteLine("The static constructor invoked."); 
    } 

    public static void Drive() 
    { 
     System.Console.WriteLine("The Drive method invoked."); 
    } 
} 

class TestBus 
{ 
    static void Main() 
    { 
     Bus.Drive(); 
    } 
} 
+0

+1 cierto y útil –

3

compilo éstos código C#:

public class MyClass1 
{ 
    public MyObject MyObject = new MyObject(); 
} 
public class MyClass2 
{ 
    public MyObject MyObject; 

    public MyClass2() 
    { 
     MyObject = new MyObject(); 
    } 
} 

que tiene ensamblaje IL:

MyClass1:

.class public auto ansi beforefieldinit test.MyClass1 
     extends [mscorlib]System.Object 
{ 
    .field public class test.MyObject MyObject 
    .method public hidebysig specialname rtspecialname 
      instance void .ctor() cil managed 
    { 
    // code size:  19 (0x13) 
    .maxstack 8 
    IL_0000: ldarg.0 
    IL_0001: newobj  instance void test.MyObject::.ctor() 
    IL_0006: stfld  class test.MyObject test.MyClass1::MyObject 
    IL_000b: ldarg.0 
    IL_000c: call  instance void [mscorlib]System.Object::.ctor() 
    IL_0011: nop 
    IL_0012: ret 
    } // end of method MyClass1::.ctor 

} // end of class test.MyClass1 

miclase2:

.class public auto ansi beforefieldinit test.MyClass2 
     extends [mscorlib]System.Object 
{ 
    .field public class test.MyObject MyObject 
    .method public hidebysig specialname rtspecialname 
      instance void .ctor() cil managed 
    { 
    // code size:  21 (0x15) 
    .maxstack 8 
    IL_0000: ldarg.0 
    IL_0001: call  instance void [mscorlib]System.Object::.ctor() 
    IL_0006: nop 
    IL_0007: nop 
    IL_0008: ldarg.0 
    IL_0009: newobj  instance void test.MyObject::.ctor() 
    IL_000e: stfld  class test.MyObject test.MyClass2::MyObject 
    IL_0013: nop 
    IL_0014: ret 
    } // end of method MyClass2::.ctor 
} // end of class test.MyClass2 

Es prefectamenta claro que la diferencia es sólo en el orden de llamada a la (Sistema constructor de la clase base. Object ::. Ctor()), inicializador MyObject (test.MyObject ::. Ctor()) y el inicializador de clase (stfld class test.MyObject test.Miclase2 :: MyObject)

En el primer caso, MyClass1 inicializa como sigue:

  • MyObject inicializador
  • clase inicializador (assigment constructor)
  • inicializador de la clase base (clase constructor base)

Pero, MyClass2 se inicializa por ese orden:

  • clase base inicializador (constructor de la clase base)
  • MiObjeto inicializador
  • inicializador de la clase (assigment constructor)
Cuestiones relacionadas