【问题标题】:Will the base class constructor be automatically called?会自动调用基类构造函数吗?
【发布时间】:2012-10-21 09:18:25
【问题描述】:
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()
{
    .............
}

【问题讨论】:

  • 从技术上讲,age 是私有成员,因此无法编译。它需要public 等才能工作。
  • 对不起,我没有意识到。但这只是为了澄清我的问题。
  • 如果你既没有为非静态构造函数指定: base(...)也没有指定: this(...),则默认为: base(),即零参数基类构造函数。你也有第一个类Person,你的Person()构造函数隐式调用Object()基类构造函数。写: base()(零参数)总是多余的。还请再次尝试您的示例,其中 Person 类构造函数采用一个或多个参数。
  • non-static 是其中的关键语句,请注意有关静态构造函数的问题:stackoverflow.com/questions/20092211/…

标签: c# constructor base-class


【解决方案1】:

这就是 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 实现来完成。

【讨论】:

  • 你说的是真的。但是,除了将 SomeNumber 设置为随机数的参数化构造函数之外,没有什么可以阻止 Base 使用无参数构造函数。 AlwaysThreeDerived 仍将使用 base(3) 调用,但另一个类(称为 RandomDerived)可以从 base 派生而无需指定无参数的 Base 构造函数。
  • 正确 - 我只是简单地举例说明显式调用基本构造函数的最常用原因。
  • 同意(和 +1)。只是不想对自称新手的人隐瞒这一点。 :)
  • 奇怪的是,Visual Studio 在 Base 构造函数上显示 0 个引用。这可能会导致错误的代码解释。
  • 要明确一点,对于“public () : base()”,base() 完全是多余的,对吗?还是在某些情况下您需要专门调用无参数构造函数?
【解决方案2】:

是的,将自动调用基类构造函数。当存在不带参数的构造函数时,无需显式调用base()

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

【讨论】:

  • 如果基础构造函数有参数,那么它也会被自动调用吗?
  • @variable 不,因为编译器不知道你想为构造函数的参数传递什么值。
  • 但是其中一个答案说:“如果您没有默认的无参数构造函数,那么将需要调用带有参数的构造函数”。对吗?
  • @variable 是的,没错。编译器拥有为您调用base() 所需的所有信息,但它不知道为非默认参数插入什么。
【解决方案3】:

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

class Person
{
    public Person(string random)
    {

    }
}

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

    }
}

【讨论】:

    【解决方案4】:

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

    如果您向基类添加了带参数的构造函数,并且未添加显式默认构造函数,则仅需要在派生类中使用“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)
        {
        }
    }
    

    【讨论】:

    • 你能解释一下案例5吗?这很混乱。
    • 抱歉,我更新了代码以使其更简单。
    【解决方案5】:

    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 但没有传递任何值。

    希望这会对您有所帮助。

    【讨论】:

      【解决方案6】:

      我没有什么要补充的,但我发现我需要调用 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
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-05-22
        • 2015-03-15
        • 2016-07-19
        • 2021-10-03
        • 1970-01-01
        • 2011-02-12
        • 2018-07-21
        • 2014-11-17
        相关资源
        最近更新 更多