2010-07-16 11 views
11

Me sorprendió descubrir que el constructor sin parámetros de mi clase base se llama cada vez que llamo al cualquier constructor en una clase derivada. Pensé que eso era para lo que : base() era para, explícitamente llamar al constructor base si y cuando quisiera.¿Cómo puedo decirle a la clase heredada que no llame al constructor sin parámetros de su clase base?

¿Cómo puedo evitar que se llame al constructor base cuando instalo una clase derivada?

using System; 

namespace TestConstru22323 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Customer customer = new Customer("Jim", "Smith"); 
      Customer c = new Customer(); 
      Console.WriteLine(customer.Display()); 

      Console.ReadLine(); 
     } 
    } 

    public class Person 
    { 
     public Person() 
     { 
      Console.WriteLine("I don't always want this constructor to be called. I want to call it explicitly."); 
     } 
    } 

    public class Customer : Person 
    { 
     private string _firstName; 
     private string _lastName; 

     //public Customer(string firstName, string lastName): base() //this explicitly calls base empty constructor 
     public Customer(string firstName, string lastName) //but this calls it anyway, why? 
     { 
      _firstName = firstName; 
      _lastName = lastName; 
     } 

     public string Display() 
     { 
      return String.Format("{0}, {1}", _lastName, _firstName); 
     } 
    } 
} 
+0

¿Cuál es la razón por la que no desea que se llame al constructor base? –

+0

Por ejemplo, si todas las clases heredadas necesitan alguna inicialización común, entonces la pongo en el constructor base, pero si algunas de ellas * no * necesitan esta inicialización, entonces quiero poder tener las que lo necesitan * explícitamente * llama al constructor base. –

+5

Si tiene una instancia en la que solo desea una parte de la funcionalidad de la clase base, probablemente necesite cambiar su jerarquía de herencia para dividir esos conjuntos de clases derivadas en dos ramas a partir de una base común más simple. –

Respuesta

25

La única manera de hacerlo es decirle explícitamente lo que otra ctor base que desea que llamar; lo que por supuesto significa que debe elegir alguna base de datos para llamar.

Usted no puede tenerlo sin llamar ctor de base en todos - conceptualmente, la construcción de un Customer se realiza por primera vez la construcción de un Person, y luego hacer Customer construcción específica en la parte superior de la misma. Por ejemplo, suponga que Person tiene campos private - ¿cómo se construirían correctamente si se permitiera la construcción de un Customer a no primero construya un Person?

+3

+1 para abordar directamente el hecho de que, lógicamente, se debe llamar a un constructor base *. –

+0

Supuse * construcción de la clase * y * llamar al constructor * fueron dos acciones separadas hechas por el compilador, por ejemplo, si pongo un campo privado en la clase base y paso por la construcción con el depurador, * primero * define el campo privado * luego * llama al constructor. Simplemente asumí que definía la variable privada y luego, solo si especificaba explícitamente que 'base()' debería llamarse, ¿llamaría al constructor base? –

+1

entonces cuando alguna vez usaría ': base()', ya que, la única vez que no se llamaría al constructor base sin parámetros es si, como mencionaste, debía especificar otro constructor para llamar, p. ': base (firstName, lastName)', o para decirlo de otra manera, si alguna vez ves ': base()' puedes eliminarlo de manera segura, ya que siempre es redundante, ¿no? –

0

Una forma sería la de hacer que su constructor por defecto de base privada, pero que sólo funciona si haces un constructor de ayudante en la clase base que llama a la privada uno cuando lo necesite de forma explícita.

class Base 
{ 
    private Base() { ... special default base logic ... } 
    protected Base(... params ...) : this() { ... exposed base that calls private cons ... } 
    protected Base(... other params ...) /* no default call */ { ... exposed cons that doesnt call default ...} 
} 

class DerivedNoCall 
{ 
    public DerivedNoCall() : base(... other params ...) {} //<-- will NOT call default base 
} 

class DerivedDoCall 
{ 
    public DerivedDoCall() : base(... params ...) {} //<-- WILL call default base cons indirectly 
} 

Esto es realmente artificial, y @aakashm tiene la mejor respuesta.

+1

Si hace que un constructor sea privado en la clase base, no se puede invocar desde la clase secundaria, explícita o implícitamente. –

+0

Corregir @Steven, pero podrías crear un constructor de ayuda protegido no predeterminado que llame al privado. –

+0

@Steven correcto, y en el ejemplo anterior aparece el error de que 'Person.Person() es inaccesible debido a su nivel de protección' –

0

Su requisito de que solo necesita llamar al constructor para algunos objetos derivados no está en línea con los principios orientados a objetos. Si es una Persona, entonces debe construirse como tal.

Si necesidad compartida de inicialización para algunos objetos, entonces debería considerar la creación de un método Initialize o añadiendo un constructor con parámetros que será llamado por los objetos derivados específicos.

El enfoque constructor parametrizado se siente un poco incómodo:

public abstract class Person 
{ 
    protected Person() 
    { 
    } 

    protected Person(bool initialize) 
    { 
     if (initialize) 
     { 
      Initialize(); 
     } 
    } 

    // ... 
} 

public class Customer : Person 
{ 
    public Customer(string firstName, string lastName) 
    { 
     // ... 
    } 
} 

public class Employee : Person 
{ 
    public Customer(string firstName, string lastName) : base(true) 
    { 
     // ... 
    } 
} 
0

Puede crear el constructor base predeterminado protected, luego tener solo constructores no predeterminados para la base y su hijo.

edición

Se podría dar la base un constructor protected con una firma diferente (como un tipo de enumeración protegido), y poner su código de inicialización de allí, mientras que el constructor por defecto, también protected, no lo hace inicialización particular.

8

En .NET, se llamará a cada constructor de objetos en una jerarquía de objetos independientemente de si llama al :base() o no.

:base() se llama implícitamente si no lo llama explícitamente.

:base() se puede utilizar si desea llamar a un contructor diferente en un objeto primario en lugar del constructor predeterminado.

Si tiene un código en el constructor padre que no se debe invocar cada vez, puede ser mejor mover ese código a su propio método que tendrá que ser llamado explícitamente después de construir el objeto. O bien, cree un constructor parametrizado en el objeto principal y use el parámetro para determinar si el código debe ejecutarse o no.

Por ejemplo:

:base(true) - This executes your code. 
:base(false) - This does not execute your code. 
2

Como otros han señalado, una instancia derivada debe llamar a llamar a uno de sus constructores de clase de base.

Si desea controlar la ejecución de una clase base lógica de inicialización, retire su constructor predeterminado y reemplazarlo con algo como esto:

public class Base { 
    // Base has no default constructor 
    public Base(bool initialize) { 
     if (initialize) { 
      // ... logic here 
     } 
    }  
} 

Y los constructores derivados aspecto:

// Derived1 executes the initialization logic 
public Derived1(): base(true) {} 

// Derived2 does not 
public Derived2(): base(false) {} 
+1

También podría considerar hacer que el constructor tome una bandera 'initialize' protegida. –

0

Cuando crea una instancia de una clase, todas las clases en la jerarquía de herencia se instancian comenzando desde el Padre superior al hijo inferior, deteniéndose en el tipo que creó la instancia. Entonces, si crea una instancia de System.Text.StringBuilder(), primero llama al constructor predeterminado de System.Object, luego StringBuilder(). Puede usar la palabra clave base para llamar a un constructor diferente (uno con igual o menos params), pero no más params. Si no especifica base, se llama al constructor predeterminado, por lo tanto, la razón por la que esto le está sucediendo. También puede usar la palabra clave base en el contexto de un método de instancia para llamar a implementaciones base, como en el caso del patrón de decorador. Si no desea llamar a los constructores privados de la clase base, debe configurar los elementos secundarios con constructores privados predeterminados y también llamar explícitamente a base (params) en sus otros constructores.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 

namespace ConsoleApplication2 
{ 
    class ProcessCaller 
    { 
     static void Main() 
     { 
      MyDerived md1 = new MyDerived(1); 
      object o = new System.Text.StringBuilder();//This will implicitly instantiate the classes in the inheritance hierarchy: object, then stringbuilder 
     } 
    } 

public class MyBase 
{ 
    int num; 

    public MyBase() 
    { 
     Console.WriteLine("in MyBase()"); 
    } 

    public MyBase(int i) 
    { 
     num = i; 
     Console.WriteLine("in MyBase(int i)"); 
    } 

    public virtual int GetNum() 
    { 
     return num; 
    } 
} 

public class MyDerived: MyBase 
{ 
    //set private constructor. base(i) here makes no sense cause you have no params 
    private MyDerived() 
    { 

    } 

    // Without specifying base(i), this constructor would call MyBase.MyBase() 
    public MyDerived(int i) : base(i) 
    { 
    } 
    public override int GetNum() 
    { 
     return base.GetNum();//here we use base within an instance method to call the base class implementation. 
    } 
} 

} 
+0

"Puede usar la palabra clave base para llamar a un constructor diferente (uno con menos params), pero no a más params". ¿Desde cuando? –

+0

@Steven Sudit Excluye la posibilidad de valores predeterminados de codificación rígida, como la base de ejemplos de Jeff (verdadera). –

+2

No veo por qué deberíamos excluir esta posibilidad. En todo caso, es bastante común escribir un constructor con múltiples parámetros, luego tener constructores con menos parámetros que llamen al principal con valores predeterminados. Esto puede volverse menos común con VS 2010, gracias a los parámetros predeterminados. –

Cuestiones relacionadas