2011-08-18 18 views
5

se ejecuten los constructores en el orden de arriba a abajo I.E. base primero seguido por derivado. Esta disposición se basa en una importante garantía de OOP de que un objeto (base aquí) siempre debe inicializarse antes de que pueda ser utilizado (aquí en el constructor de la clase derivada).¿Por qué se ejecutan los inicializadores de campo de una clase derivada antes de que los inicializadores de las clases base

Me pregunto por qué los inicializadores de campo no siguen este principio en C#? ¿Me estoy perdiendo de algo?

He encontrado una utilidad de este principio con los inicializadores de campo también. Tengo una clase base con una propiedad que devuelve un objeto Identity. Cada clase derivada tiene su propio campo de repositorio que he estado inicializando usando el inicializador de campo (usando el constructor predeterminado). Recientemente, he decidido que la clase de repositorio también se debe proporcionar con el objeto Identity, por lo que introduje un argumento adicional en el constructor del repositorio. Pero estoy atascado para averiguar:

public class ForumController : AppControllerBase 
{ 
     ForumRepository repository = new ForumRepository(Identity); 
    // Above won't compile since Identity is in the base class. 

    // ... Action methods. 
} 

Ahora me queda sólo una opción que se va a rellenar mi cada controlador con un constructor por omisión solamente para hacer el trabajo de inicialización del objeto de repositorio con Identidad.

+0

No, la razón por la que no compila no es para nada que 'Identity' está en la clase base, es simplemente porque es un miembro de instancia. Entonces, la pregunta en el título no es relevante para lo que estás tratando de hacer ... – Guffa

Respuesta

6

¿Por qué los inicializadores de campo de una clase derivada se ejecutan antes de los inicializadores de campo de la clase base?

Buena pregunta. Respondí a su pregunta en estas entradas de blog a partir de 2008:

http://blogs.msdn.com/b/ericlippert/archive/2008/02/15/why-do-initializers-run-in-the-opposite-order-as-constructors-part-one.aspx

http://blogs.msdn.com/b/ericlippert/archive/2008/02/18/why-do-initializers-run-in-the-opposite-order-as-constructors-part-two.aspx

0

Los inicializadores de campo se ejecutan en el constructor, y como el constructor de la clase base se llama primero, todos los inicializadores de campo también se ejecutan antes de que se ejecute el constructor derivado.

Ejemplo:

public class Base { 

    // field initialiser: 
    private string _firstName = "Arthur"; 

    public string FirstName { get { return _firstName;}} 
    public string LastName { get; private set; } 

    // initialiser in base constructor:  
    public Base() { 
    LastName = "Dent"; 
    } 

} 

public class Derived : Base { 

    public string FirstNameCopy { get; private set; } 
    public string LastNameCopy { get; private set; } 

    public Derived() { 
    // get values from base class: 
    FirstNameCopy = FirstName; 
    LastNameCopy = LastName; 
    } 

} 

prueba:

Derived x = new Derived(); 
Console.WriteLine(x.FirstNameCopy); 
Console.WriteLine(x.LastNameCopy); 

de salida:

Arthur 
Dent 
+0

Creo que no entendiste mi punto. Ya sé lo que estás diciendo.Mi pregunta es que, dados los inicializadores de campo en las clases base y derivadas, ¿por qué el de una clase derivada se ejecuta primero y luego el de base? Si ves constructores, es exactamente lo contrario. –

+0

@Varun K: Ya veo, está intentando acceder a un campo desde un inicializador de campo. No puede hacer eso, porque no puede usar miembros de instancia de un inicializador, y eso no tiene nada que ver con que los campos estén en clases diferentes. – Guffa

+0

Sé que no puedo hacer eso :-). pero quería saber la razón detrás de eso. –

0

Inicializadores de campo no están destinados a ser un reemplazo para los constructores.

Según la documentación de MSDN, todos los inicializadores de campo se ejecutan antes que los constructores. Sin embargo, una restricción en el inicializador de campo es que no pueden referirse a otros campos de instancia. (http://msdn.microsoft.com/en-us/library/ms173118(v=vs.80).aspx)

La restricción se debe al hecho de que no hay forma de identificar el orden correcto y las dependencias para ejecutar los Inicializadores de campo a nivel del compilador.

Tendría que escribir un constructor predeterminado para lograr lo que desea. Sin embargo, podrías probar con un campo estático; es una mala práctica y tal vez ni siquiera se adapte a su diseño.


Editar para aclarar pregunta planteada en el comentario:

En lo que se refiere a una clase derivada, los campos de clase de base tienen el mismo aspecto si se inicializan a través de la inicialización o el constructor. Esta información no se puede suministrar a las clases secundarias ya que esto implicaría exponer la implementación de la clase base (que es estrictamente privada a la clase base).

Esto implica que el inicializador de clase derivado no tiene forma de averiguar si todos los campos de la clase base se han inicializado antes de acceder a ellos. Por lo tanto, el comportamiento no está permitido.

+0

Ya conozco este punto de restricción (orden correcto y dependencia) pero si lo ve, esto es estrictamente aplicable a los campos de la misma clase. Estoy cuestionando a la vista de los inicializadores de campos en la base frente al inicializador de campo en la clase derivada –

2

En C#, un constructor se ejecuta la secuencia:

 
    Perform all field initializers 
    Chain to base class constructor 
    Execute user-supplied code for constructor 

En vb.net, la secuencia es:

 
    Chain to base class constructor 
    Perform all field initializers 
    Execute user-supplied code for constructor 

No hay Framework-bas Por eso C# hace las cosas en el orden en que lo hace, como lo demuestra el hecho de que vb.net puede y lo hace en una secuencia diferente. La justificación del diseño para el enfoque de C# es que no debería haber posibilidad de que un objeto se exponga al mundo exterior antes de que se hayan ejecutado todos los inicializadores de campo (tanto para los campos derivados como para los de clase base); dado que un constructor de clase base podría exponer un objeto al mundo exterior, hacer cumplir ese requisito significa que los inicializadores de campo deben ejecutarse antes que el constructor de la clase base.

Personalmente, no encuentro esta justificación particularmente convincente. Hay muchos escenarios en los que no será posible establecer campos en valores útiles sin tener información que no estará disponible antes de que se ejecute el constructor base. Cualquier código cuyo constructor base pueda exponer instancias parcialmente construidas necesita estar preparado para esa posibilidad. Si bien hay ocasiones en las que sería útil especificar que un inicializador de campo debería ejecutarse "temprano", creo que hay muchas más situaciones en las que es útil que puedan acceder al objeto de aprendizaje (en cierta medida porque creo que los campos de clase cuyos valores deben considerarse como invariantes durante el tiempo de vida de una instancia de clase deben, cuando sea práctico, establecerse declarativamente a través de inicializadores en lugar de imperativamente dentro de un constructor).

Incidentalmente, una característica que me gustaría ver tanto en vb.net como en C# sería un medio de declarar campos inicializados por parámetros y pseudo-campos. Si una clase tiene un campo inicializado por parámetro de un cierto nombre y tipo, cada constructor para esa clase que no encadena a otro de la misma clase debe contener parámetros con los nombres y tipos apropiados. Los valores de esos campos se establecerían en el constructor antes de que se haga cualquier otra cosa, y serían accesibles a otros inicializadores de campo. Los pseudo-campos se comportarían sintácticamente como campos, excepto que se usarían solo dentro de los inicializadores de campo, y se implementarían como variables locales dentro de los constructores. Tal característica haría que muchos tipos de estructuras fueran más convenientes. Por ejemplo, si se supone que un tipo para mantener una instancia de matriz particular, durante su vida útil, ser capaz de decir:

 
    readonly param int Length; 
    readonly ThingType[] myArray = new ThingType[Length]; 

parecería más agradable que tener que o bien la construcción de la matriz en el constructor de la clase (que no podría suceder hasta después de que se haya ejecutado el constructor base) o (para vb.net) tener que pasar la longitud a un constructor de clase base que podría usarlo para establecer un campo (que luego ocuparía espacio en la clase, incluso si su valor-- como Length arriba - puede ser redundante).

Cuestiones relacionadas