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()
{
.............
}
これは、単に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
実装で実行できます。
はい、基本クラスコンストラクターは自動的に呼び出されます。引数のないコンストラクターがある場合、base()
への明示的な呼び出しを追加する必要はありません。
これは、構築後に顧客の年齢を印刷することで簡単にテストできます( デモ付きのideoneへのリンク )。
デフォルトのパラメーターなしのコンストラクターがない場合は、パラメーター付きのコンストラクターを呼び出す必要があります。
class Person
{
public Person(string random)
{
}
}
class Customer : Person
{
public Customer(string random) : base (random)
{
}
}
base()
はデフォルトで呼び出されますが、次のような他の目的に使用できます。
例えば:
ケース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;
}
}
ここでは、いくつかのintilizationsを実行するためにbase()によって引数なしのコンストラクタを手動で呼び出していますが、値を渡していません。
これがお役に立てば幸いです。
基本クラスと派生クラスを使用するc#では、いくつかの暗黙的OR派生クラスから基本クラスの一部のコンストラクターへの明示的な呼び出しが必要です。
私はその事実に気付くまで、これらすべてがどのように機能するかを理解していませんでした。
つまり、基本クラスを派生クラスに接続する場合、派生から基本クラスでいくつかのコンストラクターを呼び出す必要があります。基本クラスは、基本クラスのコンストラクターの呼び出しを介して、常に派生クラスから常に最初にインスタンス化されます。 C#は、それがデフォルトのコンストラクタであるか、パラメータを持つデフォルト以外のコンストラクタであるかを気にしません。これが、パラメーターを持つ他の非コンストラクターが基本クラスに追加されない場合にのみ暗黙的に呼び出されるため、すべてのクラスでデフォルトコンストラクターを省略できる理由です。
パラメータを持つデフォルト以外のコンストラクタを突然追加すると、デフォルトの非表示のデフォルトコンストラクタチェーンの作成と呼び出しが中断されます。デフォルト以外のコンストラクターを使用する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.基本クラスのデフォルトコンストラクターへの呼び出しは、基本クラスで暗黙的に作成され、派生クラスから暗黙的に呼び出されます。 。base(myparamter)を使用して、デフォルト以外のパラメーターベースのコンストラクターへの明示的な呼び出しがあります
追加することはあまりありませんが、1つのケースでパラメーターなしでMyConstructor():base()を呼び出す必要があることがわかりました。 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
}