是否会自动调用基类构造函数?

时间:2012-10-31 19:16:58

标签: c# constructor base-class

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

6 个答案:

答案 0 :(得分:80)

这就是C#的工作方式。将按照Most Base - >的顺序调用类型层次结构中每种类型的构造函数。最衍生的。

因此,在您的特定实例中,它会在构造函数命令中调用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实现来执行该参数。

答案 1 :(得分:45)

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

您可以通过在施工后打印出客户的年龄(link to ideone with a demo)来轻松测试。

答案 2 :(得分:11)

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

class Person
{
    public Person(string random)
    {

    }
}

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

    }
}

答案 3 :(得分:3)

base() is called by default but it can be used for other purpose such as :

  1. base()` method is used to pass the value to the parent class construct or
  2. to call the no-arg constructor of parent class .

for example:

Case 1: if parent have parametrized constructor but not default or no-arg constructor.

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

Case 2: when parent have more than one constructor along with default one.

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

Here we are calling a no-arg constructor manually by base() to perform some intilizations but doesn'e passed any value.

Hope this will help you.

答案 4 :(得分:2)

在使用基类和派生类的c#中,必须有一些隐含或明显的调用来自派生类的基类中的某些构造函数。

直到我意识到这一点,我才明白这一切是如何奏效的。

换句话说,当您将基类连接到派生类时,必须在派生的基类中调用某些构造函数。始终通过调用基类中的某个构造函数,首先从派生类实例化基类。 C#不关心它是默认构造函数还是带参数的非默认构造函数。这就是为什么你可以在所有类中省略默认构造函数,因为只有在基类中没有添加带参数的其他非构造函数时才会隐式调用它。

当你突然添加一个带有参数的非默认构造函数时,它会破坏默认的隐藏默认构造函数链创建和调用。在具有非默认构造函数的Base类中,您现在必须从派生类显式调用该构造函数,或者在基类中显式添加默认构造函数。

让我们测试一下......

// 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)

显式调用非默认的,基于参数的构造函数
  • 什么令人困惑的是何时以及为什么默认构造函数在基类中创建并从派生类调用。只有在基础中没有出现非默认构造函数时才会发生这种情况。

答案 5 :(得分: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
}