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

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

Customer customer = new Customer(); 

Czy wiek klienta wynosi 2? Wygląda na to, że konstruktor klasy podstawowej zostanie wywołany bez względu na wszystko. Jeśli tak, dlaczego musimy czasem dzwonić pod numer base?Czy zostanie automatycznie wywołany konstruktor klasy podstawowej?

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

Pod względem technicznym "wiek" jest elementem prywatnym, więc nie zostałby skompilowany. Musi to być "public", itp. Do działania. –

+0

Przepraszam, nie zdawałem sobie z tego sprawy. Ale to tylko wyjaśnia moje pytanie. –

+5

Jeśli nie podasz ani ': base (...)', ani ': this (...)' dla niestatycznego konstruktora, domyślną wartością jest ': base()', czyli konstruktor klasy podstawowej o zerowym parametrze. Masz to również z pierwszą klasą 'Person', gdzie twój konstruktor' Person() 'niejawnie wywołuje konstruktor klasy bazowej' Object() '. Pisanie ': base()' (zero argumentów) jest zawsze zbędne. Spróbuj również ponownie, gdy konstruktor klasy "Osoba" bierze jeden lub więcej parametrów. –

Odpowiedz

51

To jest po prostu sposób działania C#. Konstruktory dla każdego typu w hierarchii typów będą wywoływane w kolejności Most Base -> Most Derived.

W tym konkretnym przypadku wywołuje Person(), a następnie Customer() w zleceniach konstruktora. Powodem, dla którego czasami trzeba używać konstruktora base, jest sytuacja, gdy konstruktory poniżej bieżącego typu wymagają dodatkowych parametrów. Na przykład:

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

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

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

Aby zbudować obiekt AlwaysThreeDerived, ma on konstruktor bez parametrów. Jednak typ Base nie. Aby utworzyć konstruktor bez parametrów, musisz dostarczyć argument do podstawowego elementu, który możesz wykonać za pomocą implementacji base.

+0

To, co mówisz, jest prawdą. Jednak nic nie stoi na przeszkodzie, aby baza miała konstruktor bez parametrów, oprócz sparametryzowanego konstruktora, który ustawił SomeNumber na, powiedzmy, liczbę losową. AlwaysThreeDerived nadal będzie używał wywołania base (3), ale inna klasa (nazwijmy to RandomDerived) może wywodzić się z bazy bez określania tego konstruktora Baseless bez parametrów. –

+0

Poprawnie - po prostu podałem krótki przykład najczęstszego powodu, aby jawnie wywołać konstruktora bazowego. – Tejs

+0

Uzgodnione (i +1). Po prostu nie chciałem tego ukrywać przed samozwańczym nowicjuszem. :) –

37

Tak, konstruktor klasy podstawowej zostanie wywołany automatycznie. Nie musisz dodawać wyraźnego połączenia do base(), gdy istnieje konstruktor bez argumentów.

Możesz to łatwo przetestować, drukując wiek klienta po zakończeniu budowy (link to ideone with a demo).

9

Jeśli nie ma domyślnego konstruktora bez parametrów wtedy nie byłoby potrzeby, aby zadzwonić do jednego z parametrów:

class Person 
{ 
    public Person(string random) 
    { 

    } 
} 

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

    } 
} 
+1

Tak, właśnie tego szukałem, dzięki. – DrNachtschatten

0

nie mam wiele do dodania, ale odkryłem, że muszę wywołaj MyConstructor(): base() bez żadnych parametrów w 1 przypadku. Mam klasy bazowej, która implementuje INotifyPropertyChanged w sposób, w którym mam funkcję wirtualną RegisterProperties(). Kiedy go przesłonię, wywoływany jest w konstruktorze podstawowym. W związku z tym musiałem wywołać go w ostatnio pochodnych klasach pochodnych, ponieważ baza została najwyraźniej wywołana przed rozpoznaniem nadpisanego wirtualnego. Moje właściwości nie powiadamiają, chyba że to zrobię. Cała klasa bazowa znajduje się poniżej.

Dodałem podklasę DatabaseTraits bezpośrednio pod nią. Bez pustego wywołania base() moje właściwości nie wywołują 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# zastosowaniem zasady i klas pochodne muszą istnieć bezpośrednia lub bezpośredniego wywołania do pewnego konstruktora klasy bazowej Z uzyskanego Klasa.

Nie rozumiałem, jak to wszystko działa, dopóki nie zdałem sobie z tego sprawy.

Innymi słowy, po połączeniu klasy bazowej z klasą pochodną, ​​musi zostać wywołany pewien konstruktor w klasie bazowej z wyprowadzonej. Klasa bazowa jest zawsze tworzona najpierw z klasy pochodnej przez wywołanie do jakiegoś konstruktora w klasie bazowej. C# nie obchodzi, czy jest to domyślny konstruktor, czy też inny niż domyślny konstruktor z parametrami. Dlatego możesz pominąć domyślny konstruktor we wszystkich twoich klasach, ponieważ jest on TYLKO JEDYNY, JEŚLI żaden inny nie-konstruktor z parametrami nie zostanie dodany w klasie bazowej.

Gdy nagle dodajesz konstruktor inny niż domyślny z parametrami, łamie on domyślne ukryte tworzenie domyślnego łańcucha konstruktora i wywołania.W klasie bazowej z innym niż domyślnym konstruktorem musisz teraz wywołać ten konstruktor jawnie z klasy pochodnej lub jawnie dodać domyślny konstruktor w klasie bazowej. Test

Zróbmy to .....

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

Powodem wszystkie elementy powyższych prac jest: 1. Wywołanie konstruktora domyślnego w klasie bazowej jest domyślnie tworzony w klasie bazowej i niejawnie wywoływana z wyprowadzonego ponieważ nie non-default konstruktor został dodany do klasy bazowej lub 2. Istnieje wyraźne wywołanie niż domyślny, konstruktor parametrów opartych stosując zasadę (myparamter)

  • Co mylące jest kiedy i dlaczego def Konstruktory ault są tworzone w klasach bazowych i wywoływane z klas pochodnych. Dzieje się tak tylko wtedy, gdy w bazie nie ma żadnych niedomyślnych konstruktorów.
Powiązane problemy