Будет ли автоматически вызван конструктор базового класса?
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 конструкторы, не являющиеся стандартными.