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

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

Customer customer = new Customer(); 

Wäre das Alter des Kunden 2? Es sieht so aus, als ob der Konstruktor der Basisklasse gleich welcher Art aufgerufen wird. Wenn ja, warum müssen wir manchmal am Ende base anrufen?Wird der Basisklassenkonstruktor automatisch aufgerufen?

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

Technisch gesehen ist 'age' ein privates Mitglied, also würde das nicht kompilieren. Es müsste "öffentlich" sein usw. um zu arbeiten. –

+0

Sorry, ich habe es nicht bemerkt. Aber das ist nur zur Klärung meiner Frage. –

+5

Wenn Sie für einen nicht statischen Konstruktor weder ': base (...)' noch ': this (...)' angeben, lautet der Standardwert ': base()', d. H. Der Zero-Parameter-Basisklassenkonstruktor. Sie haben das auch mit der ersten Klasse 'Person', wobei Ihr' Person() '-Konstruktor implizit den' Object() 'Basisklassenkonstruktor aufruft. Das Schreiben von ': base()' (Nullargumente) ist immer redundant. Versuchen Sie auch Ihr Beispiel erneut, wo der 'Person'-Klassenkonstruktor einen oder mehrere Parameter akzeptiert. –

Antwort

51

So einfach funktioniert C#. Die Konstruktoren für jeden Typ in der Typhierarchie werden in der Reihenfolge Most Base -> Most Abgeleitet aufgerufen.

Also in Ihrem bestimmten Fall ruft es Person(), und dann Customer() in den Konstruktor Bestellungen. Der Grund, warum Sie manchmal den base-Konstruktor verwenden müssen, ist, wenn die Konstruktoren unterhalb des aktuellen Typs zusätzliche Parameter benötigen. Zum Beispiel:

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

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

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

Um ein AlwaysThreeDerived Objekt zu konstruieren, hat es einen parameterlosen Konstruktor. Der Typ Base tut dies jedoch nicht. Um also einen Konstruktor ohne Parameter zu erstellen, müssen Sie dem Basis-Konstruktor ein Argument übergeben, das Sie mit der Implementierung base ausführen können.

+0

Was Sie sagen, ist wahr. Es gibt jedoch nichts, was Base davon abhält, einen parameterlosen Konstruktor zusätzlich zu dem parametrisierten Konstruktor zu haben, der SomeNumber beispielsweise auf eine Zufallszahl setzt. AlwaysThreeDerived würde immer noch den Aufruf von base (3) verwenden, aber eine andere Klasse (RandomDerived nennen) könnte von der Basis abgeleitet werden, ohne diesen parameterlosen Basiskonstruktor anzugeben. –

+0

Korrekt - Ich gab nur ein kurzes Beispiel für den am häufigsten verwendeten Grund, um den Basiskonstruktor explizit aufzurufen. – Tejs

+0

Einverstanden (und +1). Ich wollte das nicht vor dem selbsternannten Newbie verstecken. :) –

37

Ja, der Basisklassenkonstruktor wird automatisch aufgerufen. Sie müssen keinen expliziten Aufruf zu base() hinzufügen, wenn ein Konstruktor ohne Argumente vorhanden ist.

Sie können dies leicht testen, indem Sie das Alter des Kunden nach der Konstruktion ausdrucken (link to ideone with a demo).

9

Wenn Sie keine Standardparameterlos Konstruktor dann gäbe es eine Notwendigkeit, rufen Sie die man mit Parametern haben:

class Person 
{ 
    public Person(string random) 
    { 

    } 
} 

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

    } 
} 
+1

Yep, das ist, was ich gesucht habe, danke. – DrNachtschatten

0

Ich habe nicht viel hinzuzufügen, aber ich habe festgestellt, dass ich brauche, um Rufen Sie MyConstructor() auf: base() ohne Parameter in 1 Fall. Ich habe eine Basisklasse, die INotifyPropertyChanged so implementiert, dass ich eine virtuelle Funktion RegisterProperties() habe. Wenn ich es überschreibe, wird es im Basiskonstruktor aufgerufen. Daher muss ich es in den zuletzt abgeleiteten Unterklassen aufrufen, da die Basis anscheinend aufgerufen wurde, bevor das überschriebene virtuelle Objekt erkannt wurde. Meine Eigenschaften werden nicht benachrichtigt, wenn ich dies nicht tue. Die gesamte Basisklasse ist darunter.

Ich habe eine DatabaseTraits-Unterklasse direkt darunter hinzugefügt. Ohne den Aufruf von base() rufen meine Eigenschaften OnPropertyChanged() nicht auf.

[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

in C# unter Verwendung einer Base und abgeleitete Klassen müssen es einig impliziter oder expliziter Aufruf SOME CONSTRUCTOR in der Basisklasse aus der abgeleiteten Klasse.

Ich habe nicht verstanden, wie das alles funktionierte, bis ich diese Tatsache erkannte.

Mit anderen Worten, wenn Sie eine Basisklasse mit einer abgeleiteten Klasse verbinden, muss ein Konstruktor in der Basisklasse aus dem abgeleiteten aufgerufen werden. Die Basisklasse wird immer zuerst von der abgeleiteten Klasse über einen Aufruf an einen Konstruktor in der Basisklasse instanziiert. C# ist es egal, ob es sich um einen Standardkonstruktor oder einen nicht standardmäßigen Konstruktor mit Parametern handelt. Aus diesem Grund können Sie in allen Klassen einen Standardkonstruktor als implizit aufgerufenen NUR ignorieren, wenn in der Basisklasse kein anderer Nichtkonstruktor mit Parametern hinzugefügt wird.

Wenn Sie plötzlich einen nicht standardmäßigen Konstruktor mit Parameter (n) hinzufügen, wird die standardmäßige ausgeblendete Standardkonstruktorkettenerzeugung und -aufrufe unterbrochen.In Ihrer Basisklasse mit einem nicht standardmäßigen Konstruktor müssen Sie diesen Konstruktor jetzt entweder explizit von der abgeleiteten Klasse aufrufen oder einen Standardkonstruktor explizit in der Basisklasse hinzufügen.

Lassen Sie uns Test dieses .....

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

Der Grund alle Elemente über Arbeit ist entweder: 1. Der Aufruf der Standard-Konstruktor in der Basisklasse ist implizit in der Basisklasse erstellt und implizit aufgerufen aus den abgeleiteten da keine nicht-Standard-Konstruktor hat auf die Basisklasse hinzugefügt oder 2. Es gibt einen expliziten Aufruf von nicht-Standard-Parameter-basierten Konstruktor Basis (myparamter)

  • Was verwirrend ist wann und warum def ault-Konstruktoren werden in Basisklassen erstellt und von abgeleiteten Klassen aufgerufen. Das tritt nur auf, wenn KEINE nicht standardmäßigen Konstruktoren in der Basis angezeigt werden.
Verwandte Themen