スポンサーリンク
※サイト運営にサーバーは必須です※
~ ロリポップ! はコスパのよい初心者向けサーバーです~
コンストラクターが呼び出される順番
例えば、以下のようなソースコードを考えます。
このコードは、Second クラスがFirstクラス(基本クラス)を継承し、Third クラスがSecondクラスを継承しています。
ここで、Thirdクラスのインスタンスを生成した時に、どの順番でコンストラクターは呼ばれるでしょうか?
選択肢
1:First→Second→Thirdの順にコンストラクターが呼ばれる。
2:Third→Second→Firstの順にコンストラクターが呼ばれる。
正解から言えば、1.First→Second→Thirdの順にコンストラクターが呼ばれます。
ソースコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Constructor10 { class First { public First() { Console.WriteLine("Firstクラスのコンストラクターが呼び出されました"); } } class Second : First { public Second() { Console.WriteLine("Secondクラスのコンストラクターが呼び出されました"); } } class Third : Second { public Third() { Console.WriteLine("Thirdクラスのコンストラクターが呼び出されました"); } } class Program { static void Main(string[] args) { Third th = new Third(); } } } |
実行結果
Firstクラスのコンストラクターが呼び出されました
Secondクラスのコンストラクターが呼び出されました
Thirdクラスのコンストラクターが呼び出されました
基本クラスから順に呼び出される理由
基本クラスのコンストラクターから呼び出されるのは、非常に合理的です。
※コンストラクターでは、フィールドや自動実装プロパティの値の初期化など、最初に行うべき処理がまとめられています。
仮に、派生クラスのコンストラクターの処理が、基本クラスの処理より先に行われたとしたら、どのような状況に困るでしょうか?
派生クラスのコンストラクターの処理に、基本クラスのメンバの値を使用するような処理が含まれている場合に困ります。
このように、派生クラスから基本クラスにアクセスされる可能性があります。
そのため、派生クラスのコンストラクターが実行される前に、基本クラスのコンストラクターの処理が終わっている必要があります。
引数のあるコンストラクターを宣言している場合
基本クラスで引数のあるコンストラクターを宣言した場合は、いくつか注意すべきポイントがあります。
まずは、以下のようなソースコードを見てください。
このコードでは、基本クラス(Baseクラス) と派生クラス(Derivedクラス)の両方に引数のあるコンストラクターのみを宣言しています。
ソースコード(※エラーあり)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Constructor12 { public class Base { public Base(int num) { Console.WriteLine("Baseクラスを呼び出します"); Console.WriteLine(num); } } class Derived : Base { //エラー public Derived(int num) { Console.WriteLine("Derivedクラスを呼び出します"); Console.WriteLine(num); } } class Program { static void Main(string[] args) { Derived de = new Derived(20); } } } |
一見問題ないように見えますが、エラーになります。
なぜ、エラーになるかを理解するには以下の2点の動きを理解する必要があります。
・暗黙的に用意される:base()
・暗黙的に用意されるデフォルトコンストラクター
引数にある基本クラスのコンストラクターを呼び出す場合、派生クラスのコンストラクターに:base(引数)と追加で記述する必要があります。
派生クラスのコンストラクター(引数):base(基本クラスに渡す引数)
もしも:base(引数)を記述しない場合は、コンパイラーが引数のない:base()を暗黙的に用意します。
そのため、この記事の一番最初のソースコードにおいて、:base()を明示的に書くと以下のようになります。
ソースコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Constructor15 { class First { public First() : base() { Console.WriteLine("Firstクラスのコンストラクターが呼び出されました"); } } class Second : First { public Second() : base() { Console.WriteLine("Secondクラスのコンストラクターが呼び出されました"); } } class Third : Second { public Third() : base() { Console.WriteLine("Thirdクラスのコンストラクターが呼び出されました"); } } class Program { static void Main(string[] args) { Third th = new Third(); } } } |
基本クラス(Firstクラス)の:base()は、System.Objectクラス(全てのクラスの基)のコンストラクターを見に行きます。
※Objectクラスのコンストラクターには、何かしらの処理が書かれているわけではないので、意識する必要はありません。
話はもどって、エラーとなっているソースコードを考えましょう。
派生クラスのコンストラクターに対して:base()を明示的に書くと以下のようになります。
public Derived(int i) : base()
つまり、派生クラスは、基本クラスの引数なしのコンストラクターをアクセスしようとします。
しかし、今回の場合は基本クラスに引数のないコンストラクターがないので、エラーになります。
※クラスを宣言する時に、コンストラクターがそもそもなかった場合、引数のないデフォルトコンストラクターが暗黙的に用意されます。しかし、コンストラクターを明示的に宣言すると、暗黙的に用意されていたデフォルトコンストラクターが準備されなくなります。
問題となっているソースコードを動かすには、以下のどちらかを行えば、解決します。
・基本クラスの引数付きのコンストラクターを呼び出したい場合
→派生クラスのコンストラクターに対して:base(引数)を明示的に書く
・基本クラスの引数のないコンストラクターを呼び出したい場合
→デフォルトコンストラクターが準備されないので、基本クラスに引数なしのコンストラクターを宣言する
ソースコード(基本クラスの引数付きのコンストラクターを呼び出したい場合)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Constructor13 { class Base { public Base(int num) { Console.WriteLine("Baseクラスを呼び出します"); Console.WriteLine(num); } } class Derived : Base { public Derived(int i) : base(i) { Console.WriteLine("Derivedクラスを呼び出します"); Console.WriteLine(i); } } class Program { static void Main(string[] args) { Derived de = new Derived(20); } } } |
実行結果
Baseクラスを呼び出します
20
Derivedクラスを呼び出します
20
ソースコード(基本クラスの引数のないコンストラクターを呼び出したい場合)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Constructor14 { class Base { public Base() { Console.WriteLine("引数なしのコンストラクター"); } public Base(int num) { Console.WriteLine("Baseクラスを呼び出します"); Console.WriteLine(num); } } class Derived : Base { public Derived(int i) { Console.WriteLine("Derivedクラスを呼び出します"); Console.WriteLine(i); } } class Program { static void Main(string[] args) { Derived de = new Derived(20); } } } |
実行結果
引数なしのコンストラクター
Derivedクラスを呼び出します
20
~ギャンブルに絶対儲かる必勝法があるのだろうか?~
私(サイト主)はこの疑問に対して非常に興味を持ち、プログラミングで検証してみました。
このサイトを応援してもいいかなと思う人はぜひとも購入を検討してみてください。