Will the base class constructor be automatically called?

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


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


Customer customer = new Customer();

Would the age of customer be 2? It seems like the base class's constructor will be called no matter what. If so, why do we need to call base at the end sometimes?

public Customer() : base()
{
.............
}
89257 次浏览

是的,基类构造函数将被自动调用。当构造函数没有参数时,不需要向 base()添加显式调用。

您可以很容易地通过打印出客户建设后的年龄(通过演示链接到 ideone)来测试这一点。

这就是 C # 的工作原理。类型层次结构中每个类型的构造函数将按最基本-> 最派生的顺序调用。

因此在您的特定实例中,它调用 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类型没有。因此,为了创建一个无参数构造函数,您需要向基本构造函数提供一个参数,这可以通过 base实现来实现。

如果没有默认的无参数构造函数,那么就需要调用带参数的构造函数:

class Person
{
public Person(string random)
{


}
}


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


}
}

我没有太多要添加的内容,但是我发现我需要在1种情况下调用不带参数的 MyConstruction tor () : base ()。我有一个实现 INotifyPropertyChanged 的基类,其中有一个 RegisterProperties ()虚函数。当我重写它时,它在基构造函数中被调用。所以我最终不得不在最近派生的子类中调用它,因为基类显然是在识别重写的虚拟类之前调用的。除非我这么做,否则我的财产不会通知你。下面是整个基类。

我在它的正下方添加了一个 DatabaseTraits 子类。

[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
}

使用您的例子,答案是: 是的。将为您调用基构造函数,您不需要添加基构造函数。

如果您在基类中添加了一个带参数的构造函数,而且没有添加显式的缺省构造函数,那么您只需要在派生类中使用“ Base (...)”调用。

只有在中断或中断从派生类到基构造函数的隐含构造函数调用链时,才需要进行基(...)调用。在向基类添加非默认构造函数时就会发生这种情况。

在使用基类和派生类的 c # 中,对基类的构造函数的某些调用必须始终从派生类中进行,要么由运行时调用 毫无疑问,要么由您调用 明确地。大多数情况下,运行时会在创建派生类对象时为您调用基类缺省构造函数,因此您不需要调用“ base ()”。默认情况下,实例化后的派生类将始终 IMPLICITLY 调用基类缺省构造函数。这就是为什么在大多数情况下,您不需要将“ base ()”添加到派生类的构造函数中。基类总是首先通过调用基类中的缺省构造函数从派生类实例化,除非你更改了它的构造函数(见下文)。C # 不在乎它是带参数的缺省构造函数构造函数还是非默认构造函数。在创建派生类对象时,必须在基类中调用。

这就是为什么在派生类构造函数中可以省略“ base ()”调用,而在所有类中可以省略其隐式调用的显式缺省构造函数。

隐式基类构造函数调用只有在下列情况之一为真时才为真。如果为 true,则不需要在派生类中调用“ base (...)”:

  1. 您的基类没有定义显式构造函数
  2. 基类只定义了一个显式的缺省构造函数
  3. 你的基类有一个或多个非默认的构造函数定义了参数,并且定义了一个显式的缺省构造函数。

当你突然添加一个带有参数和 NO 缺省构造函数的非默认构造函数时,它会破坏默认的隐藏缺省构造函数链创建和调用,你必须添加“ base ()”。使用非默认构造函数的 Base 类中,现在必须使用“ Base (...)”从派生类显式调用该构造函数,或者在基类中显式添加一个缺省构造函数。如果是后者,则可以避免“ base ()”调用。它被隐式地调用。

我们来测试一下。

// IMPLIED CONSTRUCTOR CALL TO THE BASE CLASS CALL WORKS NATURALLY HERE
class MyBaseClass0
{
// a default constructor added for you
}
class DerivedClass0 : MyBaseClass0
{
// an implied call to the base constructor is done for you
}


// THIS WORKS!!!
class MyBaseClass1
{
// a default constructor added for you
}
class DerivedClass1 : MyBaseClass1
{
public DerivedClass1()
{
// Here the derived class default constructor is
// created explicitly but an implied call to the
// base constructor is done for you
}
}


// AND THIS WORKS!!!
class MyBaseClass2
{
// a default constructor added for you
}
class DerivedClass2 : MyBaseClass2
{
public DerivedClass2() : base()
{
// "base()" is still optional here as implied call
// to the base constructor is done for you
}
}


// AND THIS WORKS!!!
class MyBaseClass3
{
// a default constructor added for you
}
class DerivedClass3 : MyBaseClass3
{
public DerivedClass3(int x)// base not needed
{
// an implied call to the base constructor is still done for you
}
}


// BECAUSE WE ADDED A NON-DEFAULT CONSTRUCTOR WITH
// NO EXPLICIT DEFAULT CONSTRUCTOR WE MUST USE "BASE(...)"


class MyBaseClass4
{
// need explicit default constructor or must use "base()" now


// non-default constructor added which breaks implicit call
public MyBaseClass4(string y)
{


}
}
class DerivedClass4 : MyBaseClass4
{
public DerivedClass4(int x) : base("hello")
{
// The implicit call to the default constructor is broken now
// because we added a non-default constructor to base class.
// Adding a "base()" call we have fulfilled the requirement
// that some base constructor be called in the Base Class.
}
}


// The IMPLIED default constructor call now works again
// because we added an explicit default constructor beside
// the non-default one we added. "base()" is not needed again.


class MyBaseClass5
{
public MyBaseClass5()
{
}


public MyBaseClass5(string y)
{
}
}
class DerivedClass5 : MyBaseClass5
{
public DerivedClass5(string x)
{
}
}

在默认情况下,base() 被调用,但它可以用于其他目的,例如:

  1. 方法用于将值传递给父类构造或
  2. 调用父类的 no-arg 构造函数。

例如:

案例1: 如果父节点有参数化构造函数,但没有默认或无参数构造函数。

 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;
}
}

案例2: 父函数有多个构造函数和缺省构造函数。

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;
}
}

这里我们通过 base ()手动调用一个 no-arg 构造函数来执行一些初始化,但是没有传递任何值。

希望这个能帮到你。

用 NUnit 或 xUnit 测试私有方法的最简单方法

我可以建议接下来:

  1. 保护他们
  2. 创建私有模拟类,
  3. 从我们要测试的类派生它
  4. 添加调用基方法的新公共方法。
public class Car
{
protected void StartEngine() {}


protected void StartMoving() {}


public void Drive()
{
StartEngine();
StartMoving();
}
}




public class TestClass
{
[Test]
public void Test1()
{
var mock = new MockCar();
mock.StartEngine();
mock.StartMoving();
}


private class MockCar : Car
{
public new void StartEngine()
{
base.StartEngine();
}


public new void StartMoving()
{
base.StartMoving();
}
}
}