2012-10-31 108 views
59
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() 
{ 
    ............. 
} 
+5

从技术上讲,age是个私人成员,所以不会编译。它需要“公共”等才能工作。 –

+0

对不起,我没有意识到。但这只是为了澄清我的问题。 –

+5

如果您不为非静态构造函数指定':base(...)'和':this(...)',则默认为':base()',即零参数基类构造函数。你也有第一个类Person,你的Person()构造函数隐式调用Object()的基类构造函数。编写':base()'(零参数)总是多余的。再次尝试一下'Person'类构造函数接受一个或多个参数的例子。 –

回答

51

这就是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实现来执行该参数。

+0

你说的是真的。但是,除了将SomeNumber设置为随机数的参数化构造函数外,没有任何方法可以阻止Base使用无参数构造函数。 AlwaysThreeDerived仍然会使用base(3)调用,但是另一个类(称为RandomDerived)可以从基础派生而不指定该无参数的Base构造函数。 –

+0

正确 - 我只是简单地给出了显式调用基构造函数最常用的原因的一个简单例子。 – Tejs

+0

同意(和+1)。只是不想从自称的新手那里隐藏起来。 :) –

37

是的,基类的构造函数会自动调用。如果存在不带参数的构造函数,则不需要向base()添加明确的调用。

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

9

如果你没有一个默认参数的构造函数,然后会有一个需要调用一个带参数:

class Person 
{ 
    public Person(string random) 
    { 

    } 
} 

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

    } 
} 
+1

是的,这就是我一直在寻找,谢谢。 – DrNachtschatten

0

我没有太多的补充,但我发现,我需要在1个情况下调用MyConstructor():base(),不带参数。我有一个基类实现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 
} 
0

在C#使用基类和派生类必须有一些隐式或显式调用一些CONSTRUCTOR在基类从派生的类。

我不明白这一切如何工作,直到我意识到这一事实。

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

当你突然添加一个带默认构造函数的参数时,它会打破默认构造函数链的默认创建和调用。在具有非默认构造函数的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.有使用基地,非默认,基于参数的构造函数的显式调用(myparamter)

  • 什么困惑是隐式调用何时以及为什么是def ault构造函数在基类中创建并从派生类中调用。只有在基础中没有出现非默认构造函数时才会出现这种情况。
相关问题