連載

C#入門

第11 コンストラクタとデストラクタ

(株)ピーデー
川俣 晶
2001/09/13


baseクラスのコンストラクタの呼び出し

 クラスに継承関係があるときは、継承元クラス(ベース・クラス)のコンストラクタが実行された後で、継承先クラスのコンストラクタが実行される。だが、そのとき、コンストラクタの引数は、継承先クラスのコンストラクタへ渡されるため、そのままでは継承元クラスのコンストラクタへ引数は渡せない。もし、継承元クラスに、引数の無いコンストラクタが定義されていればそれを呼び出す。だが、引数の無いコンストラクタが定義されていないクラスなら、非常に困ったことになる。

 この問題を解決するためにC#には、:thisと似ている:baseという構文が用意されている。さっそく、これを使ったサンプルソースを見てみよう。

 1: using System;
 2:
 3: namespace ConsoleApplication27
 4: {
 5:   class ClassBase
 6:   {
 7:     public int x1,y1;
 8:     public ClassBase( int x, int y )
 9:     {
10:       x1 = x;
11:       y1 = y;
12:     }
13:   }
14:   class ClassDelived : ClassBase
15:   {
16:     public int x2,y2;
17:     public ClassDelived( int x, int y ) : base( x+1, y+1 )
18:     {
19:       x2 = x;
20:       y2 = y;
21:     }
22:   }
23:   class Class1
24:   {
25:     static void Main(string[] args)
26:     {
27:       ClassDelived cd = new ClassDelived( 10, 20 );
28:       Console.WriteLine(cd.x1);
29:       Console.WriteLine(cd.y1);
30:       Console.WriteLine(cd.x2);
31:       Console.WriteLine(cd.y2);
32:     }
33:   }
34: }
“: base(…)”により、継承元クラスのコンストラクタを呼び出すサンプル・プログラム4

 ここでポイントになるのは、17行目のコンストラクタの宣言である。見て分かるとおり、普通のコンストラクタの宣言の後ろに“: base( x+1, y+1 )”という記述が付加されている。これは、baseクラス(継承元クラス)であるClassBaseのコンストラクタを呼び出すことを意味する記述である。そして引数には、“x+1”と“y+1”という2つの式が書かれているが、もちろん引数として渡されるのはこの式の計算結果で、整数が2個渡されることになる。これに該当するClassBaseのコンストラクタは8〜12行目のものであり、これが実行される。

 この流れを数値の変化として見るなら、27行目でnewの引数として指定されている数値は10と20である。もちろん、これは、17〜21行目のコンストラクタには10と20として伝達される。しかし、“: base( x+1, y+1 )”という記述により、8〜12行目のコンストラクタに伝達される引数は11と21になる。その結果が、以下の実行画面で確認できることと思う。

サンプル・プログラム4の実行結果
まず+1された引数とともに、継承元クラスのコンストラクタが実行される。

メンバ変数の初期化

 単純にクラス内のメンバ変数に初期値を入れるだけなら、コンストラクタを使うのではなく、直接初期値を指定する方法もある。例えば、“public int x;”ではなく、“public int x = 1;”と初期値を指定すれば、クラスが生成されるときにその値が自動的に代入される。以下がそれを見るためのサンプルソースである。

 1: using System;
 2:
 3: namespace ConsoleApplication28
 4: {
 5:   class Test1
 6:   {
 7:     public int x = 1;
 8:     public Test1()
 9:     {
10:       Console.WriteLine( x );
11:     }
12:   }
13:   class Test2
14:   {
15:     public int x;
16:     public Test2()
17:     {
18:       Console.WriteLine( x );
19:       x = 2;
20:       Console.WriteLine( x );
21:     }
22:   }
23:   class Test3
24:   {
25:     public int x = 1;
26:     public Test3()
27:     {
28:       Console.WriteLine( x );
29:       x = 2;
30:       Console.WriteLine( x );
31:     }
32:   }
33:   class Class1
34:   {
35:     static void Main(string[] args)
36:     {
37:       Test1 t1 = new Test1();
38:       Test2 t2 = new Test2();
39:       Test3 t3 = new Test3();
40:     }
41:   }
42: }
クラス内のメンバ変数に直接初期値を指定したサンプル・プログラム5

 まず、5〜12行目のクラスは、単純なメンバ変数の初期化の例。13〜22行目はコンストラクタ内で代入した場合の例。これを見比べると、前者の方がシンプルで分かりやすい。しかし、両方同時に使ったら何が起こるのだろうか? その疑問に答えるために、もう1つのクラスを用意した。23〜32行目は、メンバ変数の初期化とコンストラクタ内での代入の双方を行った場合の例である。

 これを実行した結果は以下のようになる。

サンプル・プログラム5の実行結果
メンバ変数に初期値を代入するだけであれば、値を直接記述した方がシンプルで分かりやすくなる。コンストラクタで代入した値はそれを上書きする。また、メンバ変数はデフォルトの初期値として0となっているのが分かる。

 実行画面の最初の数値は10行目で出力しているものである。これにより、コンストラクタ実行時には、すでにメンバ変数の値は初期化済みであることが分かる。次の2つの数値は、18行目と20行目によるものだ。18行目の時点ではデフォルト値であるゼロが入ったままで、29行目の代入によって、やっと変数の値が意図した値に変わったことが分かる。最後の2行は、28行目と30行目によるものだ。これにより、コンストラクタ実行時点ではすでに25行目で指定した値である1に初期化済みであることが分かる。コンストラクタ内の代入文(29行目)は、この値への上書きとして機能していることが分かる。

 つまり、メンバ変数の初期化はコンストラクタより先に実行されるので、コンストラクタで代入した値の方が後々まで残るということである。

privateなコンストラクタ

 ここまでのサンプルでは、すべてコンストラクタにpublicキーワードを付けてきた。もし、これにprivateキーワードを付けると何が起こるのだろうか。結論を先取りすると、コンストラクタは外部からいっさい呼び出し不可能になる。言い換えれば、newを使ったインスタンスの生成がいっさい不可能になるというのに等しい。

 これにより、インスタンスが生成できないクラスが出来てしまうわけだが、こんなクラスにも立派な用途がある。例えば、staticなメソッドだけを提供するクラスを作ったとしよう。このクラスのインスタンスを生成することは無意味である。だが、勘違いしたプログラマが、インスタンスを生成しようと試みるかもしれない。そのとき、privateなコンストラクタを付けておけば、インスタンス生成を行うソースコードはエラーになり、その時点でプログラマに間違ったことに気付くチャンスを与えることができる。

 以下は実際に、privateなコンストラクタを使った例である。

 1: using System;
 2:
 3: namespace ConsoleApplication31
 4: {
 5:   class Class2
 6:   {
 7:     private Class2() {}
 8:     public static void special( string message )
 9:     {
10:       Console.WriteLine( message );
11:     }
12:   }
13:   class Class1
14:   {
15:     static void Main(string[] args)
16:     {
17:       Class2.special("Hello!");
18:       // Class2 t = new Class2(); // エラーになる 'ConsoleApplication31.Class2.Class2()' はアクセスできない保護レベルになっています。
19:     }
20:   }
21: }
privateなコンストラクタを使用したサンプル・プログラム6

 ここで注意すべきことは、17行目のstaticなメソッド呼び出しは問題なく実行可能であるが、18行目のnewは実行できないことである。18行目先頭のコメント記号(//)を取り去ると、コンパイル時に「.Class2.Class2()' はアクセスできない保護レベルになっています。」というエラーが発生する。

 これを実行した結果は以下のようになる。

サンプル・プログラム6の実行結果
コンストラクタにprivateキーワードを付けることによって、そのクラスからのインスタンス生成を禁止することができる。

staticなコンストラクタ

 C#には、staticなメソッドやメンバ変数が存在するが、コンストラクタにもstaticなものが存在する。staticなコンストラクタは、そのクラスのインスタンスが生成されたり、staticメソッドなどが呼び出されたりする前に自動的に呼び出されるコンストラクタである。ただし、いつ呼び出されるかは厳密に規定されていないので、プログラムの実行開始時に呼び出されるという保証はない。

 staticなメソッドやstaticなメンバ変数をいろいろ活用するとき、事前にメンバ変数を初期化したいときに便利な機能である。

 実際にstaticなコンストラクタを記述したサンプルソースを以下に示す。

 1: using System;
 2:
 3: namespace ConsoleApplication32
 4: {
 5:   class Class2
 6:   {
 7:     static Class2()
 8:     {
 9:       Console.WriteLine("static Class2() called");
10:     }
11:     static public void special()
12:     {
13:       Console.WriteLine("static public special() called");
14:     }
15:   }
16:   class Class1
17:   {
18:     static void Main(string[] args)
19:     {
20:       Class2.special();
21:     }
22:   }
23: }
staticなコンストラクタを使用したサンプル・プログラム7

 これを実行した結果は以下のようになる。

サンプル・プログラム7の実行結果
インスタンスを生成していないため呼ばれないはずのコンストラクタだが、staticを指定することによりそれが実行されているのが分かる。

 20行目で、11〜14行目のメソッドを呼び出しているが、それに先だって、7〜10行目のstaticコンストラクタが実行されていることが、実行画面から分かるだろう。

 なお、staticなコンストラクタには、publicやprivateのようなアクセス制御キーワードは付けない。これは暗黙のうちに実行する以外の選択肢がないので、それを明示的にコントロールする意味がないためだ。


 INDEX
  第11回 コンストラクタとデストラクタ
    1.コンストラクタとは何か?
  2.baseクラスのコンストラクタの呼び出し
    3.デストラクタとは何か?

「C#入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間