2012-10-31 36 views
59
class Person 
{ 
    public int age; 
    public Person() 
    { 
     age = 1; 
    } 
} 

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

Customer customer = new Customer(); 

Tuổi của khách hàng là 2? Có vẻ như nhà xây dựng của lớp cơ sở sẽ được gọi là không có vấn đề gì. Nếu vậy, tại sao chúng ta cần phải gọi số base ở cuối đôi khi?Trình tạo lớp cơ sở có được gọi tự động không?

public Customer() : base() 
{ 
    ............. 
} 
+5

Về mặt kỹ thuật, 'age' là một thành viên riêng tư, do đó sẽ không biên dịch được. Nó sẽ cần phải được 'công khai', vv để làm việc. –

+0

Xin lỗi, tôi đã không nhận ra điều đó. Nhưng đó chỉ là để làm rõ câu hỏi của tôi. –

+5

Nếu bạn không chỉ rõ ': base (...)' và ': this (...)' cho một hàm tạo non-static, mặc định là ': base()' nghĩa là hàm tạo lớp cơ sở zero-parameter. Bạn cũng có với lớp đầu tiên 'Person' trong đó hàm tạo' Person() 'của bạn ngầm gọi phương thức khởi tạo của lớp cơ sở' Object() '. Viết ': base()' (đối số bằng không) luôn luôn thừa. Ngoài ra, hãy thử lại ví dụ của bạn khi hàm tạo lớp 'Person' nhận một hoặc nhiều tham số. –

Trả lời

51

Đây đơn giản là cách C# hoạt động. Các hàm tạo cho mỗi kiểu trong hệ thống phân cấp kiểu sẽ được gọi theo thứ tự của Hầu hết các cơ sở -> Có nguồn gốc nhiều nhất.

Vì vậy, trong trường hợp cụ thể của bạn, nó gọi Person() và sau đó Customer() trong các đơn đặt hàng của người tạo. Lý do tại sao bạn cần đôi khi sử dụng hàm tạo base là khi các hàm tạo bên dưới loại hiện tại cần các tham số bổ sung. Ví dụ:

public class Base 
{ 
    public int SomeNumber { get; set; } 

    public Base(int someNumber) 
    { 
     SomeNumber = someNumber; 
    } 
} 

public class AlwaysThreeDerived : Base 
{ 
    public AlwaysThreeDerived() 
     : base(3) 
    { 
    } 
} 

Để xây dựng một đối tượng AlwaysThreeDerived, nó có một hàm tạo tham số. Tuy nhiên, loại Base thì không. Vì vậy, để tạo ra một hàm tạo không cần thiết, bạn cần cung cấp một đối số cho các hằng số cơ sở, mà bạn có thể thực hiện với việc thực hiện base.

+0

Điều bạn nói là đúng. Tuy nhiên, không có gì ngăn không cho Base có một hàm tạo parameterless ngoài constructor được tham số hóa mà thiết lập SomeNumber, ví dụ, một số ngẫu nhiên. AlwaysThreeDerived vẫn sẽ sử dụng lệnh gọi cơ sở (3), nhưng một lớp khác (gọi nó là RandomDerived) có thể lấy được từ cơ sở mà không chỉ rõ hàm tạo Base không có tham số đó. –

+0

Đúng - tôi chỉ đơn giản là đưa ra một ví dụ nhanh về lý do thường được sử dụng nhất để gọi một cách rõ ràng hàm tạo cơ sở. – Tejs

+0

Đồng ý (và +1). Chỉ không muốn giấu điều đó khỏi những người mới tự xưng. :) –

37

Có, trình tạo lớp cơ sở sẽ được gọi tự động. Bạn không cần phải thêm lệnh gọi rõ ràng vào base() khi có một hàm tạo không có đối số.

Bạn có thể dễ dàng kiểm tra điều này bằng cách in tuổi của khách hàng sau khi xây dựng (link to ideone with a demo).

9

Nếu bạn không có một constructor parameterless mặc định sau đó sẽ có một nhu cầu để gọi một với các thông số:

class Person 
{ 
    public Person(string random) 
    { 

    } 
} 

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

    } 
} 
+1

vâng, đó là những gì tôi đang tìm kiếm, cảm ơn. – DrNachtschatten

0

Tôi không có nhiều thêm, nhưng tôi đã thấy rằng tôi cần phải gọi MyConstructor(): base() không có tham số trong 1 trường hợp. Tôi có một lớp cơ sở thực hiện INotifyPropertyChanged theo cách mà tôi có hàm đăng ký ảo (RegisterProperties() ảo. Khi tôi ghi đè lên nó, nó được gọi trong constructor cơ sở. Vì vậy, tôi cuối cùng phải gọi nó trong các lớp con có nguồn gốc gần đây nhất vì cơ sở đã được gọi rõ ràng trước khi ghi đè ảo được nhận ra. Thuộc tính của tôi không thông báo trừ khi tôi làm điều này. Toàn bộ lớp cơ sở là dưới đây.

Tôi đã thêm một lớp con DatabaseTraits ngay bên dưới nó. Nếu không có lệnh base() rỗng, thuộc tính của tôi không gọi 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

Trong C# sử dụng cơ sở và các lớp thừa kế phải có một số CALL ngầm hoặc rõ ràng CHO MỘT SỐ dựng trong lớp cơ sở từ lớp dẫn xuất.

Tôi không hiểu làm thế nào tất cả điều này làm việc cho đến khi tôi nhận ra thực tế đó.

Nói cách khác, khi bạn kết nối một lớp cơ sở với một lớp dẫn xuất, một số hàm tạo phải được gọi trong lớp cơ sở từ nguồn gốc. Lớp cơ sở luôn luôn được khởi tạo ngay từ lớp dẫn xuất thông qua một lời gọi đến một số hàm tạo trong lớp cơ sở. C# không quan tâm nếu nó là một constructor mặc định hoặc constructor không mặc định với các tham số. Đó là lý do tại sao bạn có thể bỏ qua một hàm khởi tạo mặc định trong tất cả các lớp của bạn vì nó được gọi là ngầm CHỈ NẾU không có non-constructor khác với tham số được thêm vào trong lớp cơ sở.

Khi bạn đột nhiên thêm một hàm tạo không mặc định với (các) tham số, nó phá vỡ việc tạo chuỗi và tạo cuộc gọi mặc định ẩn mặc định.Trong lớp cơ sở của bạn với một hàm tạo không mặc định, bây giờ bạn phải gọi hàm tạo đó một cách rõ ràng từ lớp dẫn xuất hoặc thêm một hàm tạo mặc định một cách rõ ràng trong lớp cơ sở.

Hãy kiểm tra này .....

// 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 
    //{ 
    //} 
} 

Lý do tất cả các mục trên công việc là một trong hai: 1. Các cuộc gọi đến constructor mặc định trong lớp cơ sở được mặc nhiên tạo ra trong lớp cơ sở và ngầm được gọi là từ bắt nguồn bởi vì không có nhà xây dựng không phải mặc định đã được thêm vào các lớp cơ sở hoặc 2. có một cuộc gọi rõ ràng để không phải mặc định, nhà xây dựng tham số dựa trên sử dụng cơ sở (myparamter)

  • có gì khó hiểu là khi nào và tại sao def ault constructors được tạo ra trong các lớp cơ sở và được gọi từ các lớp dẫn xuất. Điều đó chỉ xảy ra nếu NO constructor không mặc định xuất hiện trong cơ sở.
Các vấn đề liên quan