Будет ли автоматически вызван конструктор базового класса?

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

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

Customer customer = new Customer();

Будет ли возраст клиента 2? Кажется, что конструктор базового класса будет вызван, несмотря ни на что. Если да, то зачем нам иногда звонить base в конце?

public Customer() : base()
{
    .............
}

Ответ 1

Это просто, как С# будет работать. Конструкторы для каждого типа в иерархии типов будут вызываться в порядке большей базы → Most Derived.

Итак, в вашем конкретном экземпляре он вызывает Person(), а затем Customer() в заказах конструктора. Причина, по которой вам иногда нужно использовать конструктор base, - это когда конструкторы ниже текущего типа нуждаются в дополнительных параметрах. Например:

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

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

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

Чтобы построить объект AlwaysThreeDerived, он имеет конструктор без параметров. Однако тип base не работает. Поэтому, чтобы создать конструктор без параметров, вам необходимо предоставить аргумент базовому constuctor, который вы можете сделать с реализацией base.

Ответ 2

Да, конструктор базового класса будет вызываться автоматически. Вам не нужно добавлять явный вызов base(), когда есть конструктор без аргументов.

Вы можете легко протестировать это, указав возраст клиента после строительства (ссылка на ideone с демонстрацией).

Ответ 3

Если у вас не было конструктора без параметров без параметров, тогда возникнет необходимость вызвать тот, который имеет параметры:

class Person
{
    public Person(string random)
    {

    }
}

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

    }
}

Ответ 4

Мне нечего добавить, но я обнаружил, что мне нужно вызвать MyConstructor(): base() без параметров в 1 случае. У меня есть базовый класс, который реализует INotifyPropertyChanged таким образом, что у меня есть виртуальная функция RegisterProperties(). Когда я переопределяю его, он вызывается в базовом конструкторе. Поэтому мне приходится называть его в самых последних подклассах, потому что база, по-видимому, была вызвана до того, как был признан переопределенный виртуальный. Мои свойства не сообщают, если я не сделаю этого. Весь базовый класс ниже.

Я добавил подкласс DatabaseTraits непосредственно под ним. Без пустого вызова base() мои свойства не вызывают 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
}

Ответ 5

В С#, используя базовый и производный классы, ДОЛЖЕН БЫТЬ НЕКОТОРЫМ ИМПЛИКТИРУЕМОЙ ИЛИ ОПЫТНОЙ ЗВОНОК К НЕКОТОРЫМ КОНСТРУКТОРОМ В БАЗОВОМ КЛАССЕ ИЗ ПРОИЗВОДСТВЕННОГО КЛАССА.

Я не понимал, как все это сработало, пока я не осознал этот факт.

Другими словами, когда вы соединяете базовый класс с производным классом, некоторый конструктор должен вызываться в базовом классе из производного. Базовый класс всегда создается сначала из производного класса через вызов некоторого конструктора в базовом классе. С# не заботится, является ли он конструктором по умолчанию или конструктором, отличным от стандартного, с параметрами. Вот почему вы можете оставить конструктор по умолчанию во всех своих классах, так как он неявно используется ТОЛЬКО, ЕСЛИ никакой другой неконструктор с параметрами не добавлен в базовый класс.

При внезапном добавлении конструктора, отличного от значения по умолчанию, с параметром (-ами), он разрушает создание и вызовы цепочки конструктора по умолчанию, скрытые по умолчанию. В вашем базовом классе с конструктором, отличным от стандартного, вы должны либо явно вызвать этот конструктор явно из производного класса, либо явно добавить конструктор по умолчанию в базовый класс.

Позвольте проверить это.....

// 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
    //{
    //}
}

Причина, по которой все вышеперечисленные элементы: 1. Вызов конструктора по умолчанию в базовом классе неявно создается в базовом классе и неявно вызывается из производного, потому что в базовый класс не добавлен конструктор по умолчанию, отличный от стандартного, или 2. Существует явный вызов нестандартного конструктора на основе параметров, использующего base (myparamter)

  • В чем непонятно, когда и почему конструкторы по умолчанию создаются в базовых классах и вызывается из производных классов. Это происходит только в том случае, если в базе данных появляются NO конструкторы, не являющиеся стандартными.