web-dev-qa-db-fra.com

Le constructeur de la classe de base sera-t-il appelé automatiquement?

class Person
{
    public int age;
    public Person()
    {
        age = 1;
    }
}

class Customer : Person
{
    public Customer()
    {
        age += 1;
    }
}

Customer customer = new Customer();

L'âge du client serait-il 2? Il semble que le constructeur de la classe de base sera appelé quoi qu'il arrive. Si oui, pourquoi avons-nous parfois besoin d'appeler base à la fin?

public Customer() : base()
{
    .............
}
80
Ivan Li

C’est simplement ainsi que C # va fonctionner. Les constructeurs de chaque type dans la hiérarchie des types seront appelés dans l'ordre suivant: La plupart des bases -> Les plus dérivées. 

Ainsi, dans votre cas particulier, il appelle Person(), puis Customer() dans les ordres du constructeur. La raison pour laquelle vous devez parfois utiliser le constructeur base est lorsque les constructeurs situés sous le type actuel ont besoin de paramètres supplémentaires. Par exemple:

public class Base
{
     public int SomeNumber { get; set; }

     public Base(int someNumber)
     {
         SomeNumber = someNumber;
     }
}

public class AlwaysThreeDerived : Base
{
    public AlwaysThreeDerived()
       : base(3)
    {
    }
}

Pour construire un objet AlwaysThreeDerived, il a un constructeur sans paramètre. Cependant, le type Base ne le fait pas. Ainsi, afin de créer un constructeur sans paramètre, vous devez fournir un argument à l'agent de base, ce que vous pouvez faire avec l'implémentation base.

74
Tejs

Oui, le constructeur de la classe de base sera appelé automatiquement. Vous n'avez pas besoin d'ajouter un appel explicite à base() lorsqu'il existe un constructeur sans argument.

Vous pouvez facilement le tester en indiquant l'âge du client après la construction ( lien vers ideone avec une démonstration ).

44
dasblinkenlight

Si vous n'aviez pas de constructeur par défaut sans paramètre, il serait nécessaire d'appeler celui avec des paramètres:

class Person
{
    public Person(string random)
    {

    }
}

class Customer : Person
{
    public Customer(string random) : base (random)
    {

    }
}
11
Science_Fiction

base()est appelé par défaut, mais il peut être utilisé à d'autres fins, telles que:

  1. la méthode `base ()` est utilisée pour transmettre la valeur à la construction de la classe parente ou
  2. appeler le constructeur no-arg de la classe parente.

par exemple:

Cas 1: si le parent a un constructeur paramétré mais pas de constructeur par défaut ou sans constructeur.  

 class Person
 {

    private string FirstName;
    private string LastName;
    private string EmailAddress;
    private DateTime DateOfBirth;

    public Person(string firstName, string lastName, string emailAddress, DateTime dateOfBirth)
    {
        FirstName = firstName;
        LastName = lastName;
        EmailAddress = emailAddress;
        DateOfBirth = dateOfBirth;

    }
    }
class Employee : Person
{
    private double Salary { get; set; } = 0;

    public Employee(string firstName, string lastName, string emailAddress, DateTime dateOfBirth,double salary)
        :base(firstName,lastName,emailAddress,dateOfBirth)// used to pass value to parent constructor and it is mandatory if parent doesn't have the no-argument constructor.
    {
        Salary = salary;
    }
}

Cas 2: lorsque le parent a plusieurs constructeurs avec le constructeur par défaut.

class Person
{

    private string FirstName;
    private string LastName;
    private string EmailAddress;
    private DateTime DateOfBirth;

    public Person()
    {
        // some important intialization's to be done  

    }

    public Person(string firstName, string lastName, string emailAddress, DateTime dateOfBirth)
    {
        FirstName = firstName;
        LastName = lastName;
        EmailAddress = emailAddress;
        DateOfBirth = dateOfBirth;

    }
    }
class PermanentEmployee : Person
{
    public double HRA { get; set; }
    public double DA { get; set; }
    public double Tax { get; set; }
    public double NetPay { get; set; }
    public double TotalPay { get; set; }

    public PermanentEmployee(double hRA, double dA, double tax, double netPay, double totalPay) : base();
    {
        HRA = hRA;
        DA = dA;
        Tax = tax;
        NetPay = netPay;
        TotalPay = totalPay;
    }
}

Ici, nous appelons manuellement un constructeur no-arg par base () pour effectuer certaines intégrations, mais n’a transmis aucune valeur.

J'espère que ceci vous aidera. 

3
Prakash Sharma

En c # utilisant les classes de base et les classes dérivées IL DOIT AVOIR UN APPEL EXPLICITE IMPLICITE OR À UN CONSTRUCTEUR DE LA CLASSE DE BASE DE LA CLASSE DÉRIVÉE.

Je n'ai pas compris comment tout cela fonctionnait jusqu'à ce que je m'en rende compte.

En d'autres termes, lorsque vous connectez une classe de base à une classe dérivée, un constructeur doit être appelé dans la classe de base à partir de la classe dérivée. La classe de base est toujours d'abord instanciée à partir de la classe dérivée via un appel à un constructeur de la classe de base. C # ne se soucie pas de savoir s'il s'agit d'un constructeur par défaut ou d'un constructeur autre que par défaut avec des paramètres. C'est pourquoi vous pouvez laisser un constructeur par défaut dans toutes vos classes car il est appelé implicitement UNIQUEMENT SI AUCUN autre non-constructeur avec paramètre (s) n'est ajouté dans la classe de base.

Lorsque vous ajoutez soudainement un constructeur non défini par défaut avec un ou plusieurs paramètres, la création et les appels de la chaîne de constructeurs par défaut masqués par défaut sont interrompus. Dans votre classe de base avec un constructeur autre que celui par défaut, vous devez maintenant appeler ce constructeur explicitement à partir de la classe dérivée ou ajouter explicitement un constructeur par défaut dans la classe de base.

Testons cela .....

// THIS WORKS!!!
class MyBaseClass0
{
    // no default constructor - created automatically for you
}
class DerivedClass0 : MyBaseClass0
{
    // no default constructor - created automatically for you and calls the base class default constructor above
}

// THIS WORKS!!!
class MyBaseClass1
{
    // same as above
}
class DerivedClass1 : MyBaseClass1
{
    public DerivedClass1()
    {
      // here the derived class default constructor is created explicitly but the call to the base class default constructor is implicitly called
    }
}

// AND THIS WORKS!!!
class MyBaseClass2
{
    // as above
}
class DerivedClass2 : MyBaseClass2
{
    public DerivedClass2() : base()
    {
       // here we explicitly call the default constructor in the base class using base(). note its optional as base constructor would be called anyway here
    }
}

// AND THIS WORKS!!!
class MyBaseClass3
{
    // no default constructor
}
class DerivedClass3 : MyBaseClass3
{
    public DerivedClass3(int x)//non-default constructor
    {
       // as above, the default constructor in the base class is called behind the scenes implicitly here
    }
}

// AND THIS WORKS
class MyBaseClass4
{
    // non default constructor but missing default constructor
    public MyBaseClass4(string y)
    {

    }
}
class DerivedClass4 : MyBaseClass4
{
    // non default constructor but missing default constructor
    public DerivedClass4(int x) : base("hello")
    {
       // note that here, we have fulfilled the requirement that some constructor be called in base even if its not default
    }
}

// BUT THIS FAILS!!!...until you either add in a base() call to the non-default constructor or add in the default constructor into base!
class MyBaseClass5
{
    // 1. EITHER ADD MISSING DEFAULT CONSTRUCTOR HERE AND CALL IT USING base() below....
    public MyBaseClass5() { }

    // 2. Or use the non-default constructor and call to base("hello") below
    //public MyBaseClass5(string y)
    //{
    //}
}
class DerivedClass5 : MyBaseClass5
{
    public DerivedClass5(int x) : base()// 1. Either ADD explicit call here to explicit default constructor in base class
    {
    }

    //public DerivedClass5(int x) : base("hello")// 2. Or ADD explicit call here to parameter-based constructor in base class
    //{
    //}
}

La raison pour laquelle tous les éléments ci-dessus fonctionnent est soit: 1. L'appel au constructeur par défaut de la classe de base est créé de manière implicite dans la classe de base et appelé implicitement à partir de la classe dérivée, car aucun constructeur autre que celui par défaut n'a été ajouté à la classe de base ou 2. Il existe un appel explicite à un constructeur basé sur des paramètres, autre que celui par défaut, utilisant base (myparamter)

  • Ce qui est déroutant, c'est quand et pourquoi les constructeurs par défaut sont créés dans des classes de base et appelés à partir de classes dérivées. Cela ne se produit que si AUCUN constructeur n'apparaît dans la base.
2
Stokely

Je n'ai pas grand chose à ajouter, mais j'ai constaté que je devais appeler MyConstructor (): base () sans paramètre dans un cas. J'ai une classe de base qui implémente INotifyPropertyChanged d'une manière où j'ai une fonction virtuelle RegisterProperties (). Lorsque je le remplace, il est appelé dans le constructeur de base. Je finis donc par l'appeler dans les dernières sous-classes dérivées, car la base était apparemment appelée avant que le virtuel substitué ne soit reconnu. Mes propriétés ne notifient pas à moins que je le fasse. La classe de base entière est en dessous.

J'ai ajouté une sous-classe DatabaseTraits directement en dessous. Sans l'appel de base () vide, mes propriétés n'appellent pas OnPropertyChanged ().

[DataContract]
public abstract class DataModelBase : INotifyPropertyChanged, IDataErrorInfo {

    #region Properties

    [IgnoreDataMember]
    public object Self {
        get { return this; }
        //only here to trigger change
        set { OnPropertyChanged("Self"); }
    }

    #endregion Properties

    #region Members

    [IgnoreDataMember]
    public Dispatcher Dispatcher { get; set; }

    [DataMember]
    private Dictionary<object, string> _properties = new Dictionary<object, string>();

    #endregion Members

    #region Initialization

    public DataModelBase() {
        if(Application.Current != null) Dispatcher = Application.Current.Dispatcher;
        _properties.Clear();
        RegisterProperties();
    }

    #endregion Initialization

    #region Abstract Methods

    /// <summary>
    /// This method must be defined
    /// </summar
    protected abstract void RegisterProperties();

    #endregion Abstract Methods

    #region Behavior

    protected virtual void OnPropertyChanged(string propertyName) {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected bool RegisterProperty<T>(ref T property, string propertyName) {
        //causes problems in design mode
        //if (property == null) throw new Exception("DataModelBase.RegisterProperty<T> : ref T property cannot be null.");
        if (_properties.ContainsKey(property)) return false;

        _properties.Add(property, propertyName);

        return true;
    }

    protected string GetPropertyName<T>(ref T property) {
        if (_properties.ContainsKey(property))
            return _properties[property];

        return string.Empty;
    }

    protected bool SetProperty<T>(ref T property, T value) {
        //if (EqualityComparer<T>.Default.Equals(property, value)) return false;
        property = value;
        OnPropertyChanged(GetPropertyName(ref property));
        OnPropertyChanged("Self");

        return true;
    }

    [OnDeserialized]
    public void AfterSerialization(StreamingContext context) {
        if (Application.Current != null) Dispatcher = Application.Current.Dispatcher;
        //---for some reason this member is not allocated after serialization
        if (_properties == null) _properties = new Dictionary<object, string>();
        _properties.Clear();
        RegisterProperties();
    }

    #endregion Behavior

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion INotifyPropertyChanged Members

    #region IDataErrorInfo Members

    string IDataErrorInfo.Error {
        get { throw new NotImplementedException(); }
    }

    string IDataErrorInfo.this[string propertyName] {
        get { throw new NotImplementedException(); }
    }

    #endregion IDataErrorInfo Members

} //End class DataModelBaseclass DataModelBase

/*I decided to add an example subclass*/
    [DataContract]
public abstract class DatabaseTraits : DataModelBase {
    #region Properties
    private long _id = -1;
    [DataMember]
    public long Id {
        get { return _id; }
        set { SetProperty(ref _id, value); }
    }
    private bool _isLocked = false;
    [DataMember]
    public bool IsLocked {
        get { return _isLocked; }
        set { SetProperty(ref _isLocked, value); }
    }

    private string _lockedBy = string.Empty;
    [DataMember]
    public string LockedBy {
        get { return _lockedBy; }
        set { SetProperty(ref _lockedBy, value); }
    }

    private DateTime _lockDate = new DateTime(0);
    [DataMember]
    public DateTime LockDate {
        get { return _lockDate; }
        set { SetProperty(ref _lockDate, value); }
    }

    private bool _isDeleted = false;
    [DataMember]
    public bool IsDeleted {
        get { return _isDeleted; }
        set { SetProperty(ref _isDeleted, value); }
    }
    #endregion Properties

    #region Initialization
    public DatabaseTraits() : base() {
        /*makes sure my overriden RegisterProperties() is called.*/
    }
    protected override void RegisterProperties() {
        RegisterProperty(ref _id, "Id");
        RegisterProperty(ref _isLocked, "IsLocked");
        RegisterProperty(ref _lockedBy, "LockedBy");
        RegisterProperty(ref _lockDate, "LockDate");
        RegisterProperty(ref _isDeleted, "IsDeleted");
    }
    #endregion Initialization

    #region Methods
    public void Copy(DatabaseTraits that) {
        Id = that.Id;
        IsLocked = that.IsLocked;
        LockedBy = that.LockedBy;
        LockDate = that.LockDate;
        IsDeleted = that.IsDeleted;
    }
    #endregion Methods
}
0
steviesama