126
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()
{
    .............
}
4

7 回答 7

115

这就是 C# 的工作方式。类型层次结构中每种类型的构造函数将按照 Most Base -> 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类型没有。因此,为了创建无参数构造函数,您需要向基本构造函数提供一个参数,您可以通过base实现来完成。

于 2012-10-31T19:20:48.833 回答
65

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

您可以通过在构建后打印出客户的年龄来轻松测试这一点(链接到带有演示的 ideone)。

于 2012-10-31T19:18:01.527 回答
16

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

class Person
{
    public Person(string random)
    {

    }
}

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

    }
}
于 2012-10-31T19:21:34.950 回答
8

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

如果您向基类添加了带参数的构造函数,并且没有添加显式默认构造函数,则仅需要在派生类中使用“base(...)”调用。

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

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

这就是为什么您可以省略派生类构造函数中的“base()”调用和所有类中的显式默认构造函数作为隐式调用的原因。

隐式基类构造函数调用仅在以下情况之一为真时为真并且为您生成。如果为真,则不需要在派生类中调用“base(...)”:

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

当您突然添加一个带有参数且没有默认构造函数的非默认构造函数时,它会破坏默认隐藏的默认构造函数链创建和调用,您必须添加“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)
    {
    }
}
于 2017-05-02T14:46:48.943 回答
7

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

  1. base()` 方法用于将值传递给父类构造或
  2. 调用父类的无参数构造函数。

例如:

情况 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() 手动调用无参数构造函数来执行一些 intilizations 但不传递任何值。

希望这会帮助你。

于 2018-05-14T17:35:13.003 回答
0

我没有太多要补充的,但我发现我需要调用 MyConstructor() : base() 在 1 种情况下没有参数。我有一个基类,它以我有一个 RegisterProperties() 虚函数的方式实现 INotifyPropertyChanged。当我覆盖它时,它在基本构造函数中被调用。所以我最终不得不在最近派生的子类中调用它,因为在识别覆盖的虚拟之前显然调用了基础。除非我这样做,否则我的财产不会通知。整个基类如下。

我在它下面直接添加了一个 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
}
于 2017-04-14T08:06:55.020 回答
0

使用 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();
        }
    }
}
于 2022-02-02T05:52:14.350 回答