2009-04-09 21 views
12

Mi problema aquí es que me gustaría pasar un objeto a una clase derivada, pero debe hacerse antes del constructor de la clase base, ya que la clase base llamará inmediatamente al derivado el método Start() de la clase que usa el objeto.Ejecutar un constructor derivado antes del constructor base en C#

Aquí hay un extracto de la clase base, (renombrado de BarcodeScanner por conveniencia).

public abstract class MyBase 
{  
    public MyBase() 
    { 
     if (Initialize()) 
      this.Start(); 
    } 

    public abstract bool Initialize(); 
    public abstract void Start(); 
} 

Aquí está la clase derivada que estoy creando.

class MyDerived : MyBase 
{ 
    private string sampleObject; 

    public MyDerived (string initObject) 
    { 
     sampleObject = initObject; 
    } 

    public override bool Initialize() 
    { 
     return GetDevice(); 
    } 
    public override void Start() 
    { 
     Console.WriteLine("Processing " + sampleObject.ToString()); 
    } 
} 

Dudo que pueda hacer que C# ejecute un constructor derivado antes del constructor base; así que realmente estoy buscando una solución para pasar un objeto a la clase derivada antes de que se use el objeto.

He solucionado esto poniendo el bloque Initialize/Start if dentro del constructor MyDerived. Sin embargo, hay otras clases que se derivan de la clase base; así que terminé teniendo que repetir este bloque de Inicializar/Código de inicio en cada clase derivada. Me gustaría ver una alternativa a la modificación de la clase base.

Respuesta

15

Lo que estás tratando de hacer es imposible en C#. Un constructor en una clase base debe ejecutarse antes que el constructor de cualquier clase derivada; de lo contrario, podría haber un estado de objeto corrupto. Un objeto hijo debe ser capaz de asumir que su base está completamente construida y disponible.

+2

Bueno, es posible crear métodos de Inicializar/Comenzar vacíos y mover esos códigos a los métodos derivedInitialize/derivedStart, y luego en el constructor derivado, llamar a 'if (derivedInitialize()) derivedStart()'. Puede que no haya soluciones elegantes, pero todavía hay otras formas. – James

+0

Si bien a menudo es bueno que un marco intente evitar la exposición de objetos construidos de forma incompleta, hay momentos en los que sería más útil para una clase tener acceso a dichos objetos, con la advertencia de que debe estar preparado para tratar con objetos parcialmente construidos. Por ejemplo, si una clase encapsulará un objeto 'IDisposable' cuya vida coincide con la suya, sería conveniente usar un inicializador de campo para configurarlo; por desgracia en C# no hay una manera limpia de hacerlo de manera segura. Si el constructor de la clase base lanza, nada podrá obtener una referencia a la cosa que necesita eliminación. – supercat

17

En mi humilde opinión, su diseño es incorrecto. No debe iniciar el proceso desde el constructor. Su código de consumo debe llamar explícitamente al método Start() cuando sea necesario.

+0

+1 Para llamadas explicitas de "Inicio()". –

+0

+1. Este tipo es correcto, necesitas .Iniciar()/.Inicializar(). No llame a los métodos virtuales en los constructores. – Quibblesome

+0

+1. C++ restringe las llamadas virtuales de los constructores por una razón. –

0

Repararé su diseño para que Initialize (y potencialmente Start() - aunque normalmente haría que sea un método público llamado por el usuario) se llame después de la construcción.

Si está creando un BarcodeScanner, puede hacerlo la primera vez que vaya a escanear. Inicialmente, inicie de forma lenta sus miembros utilizando los datos de la clase derivada.

Esto solucionará su problema, sin un cambio real en el uso por parte del usuario.

+0

El dispositivo inicia el análisis de forma asíncrona, por lo que no puede inicializar en función del inicio del escaneo. En retrospectiva, Microsoft debería haber cambiado el nombre de su método Start() para que sea el método Ready(). Dado que el método Start() solo prepara el escáner, en realidad no inicia el escaneo. – James

Cuestiones relacionadas