コンストラクタとデストラクタ
コンストラクタ
using System;
namespace ConsoleApplication36
{
class Class2
{
public Class2()
{
Console.WriteLine("public Class2()");
}
}
class Class3
{
public Class3( int x, int y )
{
Console.WriteLine("public Class3({0},{1})",x,y);
}
}
class Class1
{
static void Main(string[] args)
{
Class2 t1 = new Class2();
Class3 t2 = new Class3( 1, 2 );
}
}
}
Class2クラスのインスタンスが
POINT
引数の異なる同名のメソッドを扱う機能がある
これをオーバーロードという.
new で呼び出す Ctor は Signature で決まり
ひとつのみ.
-> C# では複数呼び出しが可能.
明示的には呼び出されていないコンストラクタが呼び出されている
baseクラスのコンストラクタの呼び出し
POINT
bCls から生成される.
そのとき
コンストラクタの引数は
継承先クラスのコンストラクタへ渡されるため、
そのままでは継承元クラスのコンストラクタへ引数は渡せない。
継承元クラスに、引数の無いコンストラクタが定義されていればそれを呼び出す。
引数の無いコンストラクタが定義されていないクラスなら、非常に困ったことになる。
この問題を解決するためにC#には、:thisと似ている:baseという構文が用意されている。
using System;
namespace ConsoleApplication27
{
class Class1
{
static void Main(string[] args)
{
ClassDelived cd = new ClassDelived( 10, 20 );
Console.WriteLine(cd.x1);
Console.WriteLine(cd.y1);
Console.WriteLine(cd.x2);
Console.WriteLine(cd.y2);
}
}
}
この流れを数値の変化として見るなら、
27行目でnewの引数として指定されている数値は10と20である。
もちろん、これは、17〜21行目のコンストラクタには10と20として伝達される。
しかし、“: base( x+1, y+1 )”という記述により、
コンストラクタに伝達される引数は11と21になる。
まず+1された引数とともに、継承元クラスのコンストラクタが実行される。
VarMem の初期化は宣言時に代入してしまう. EX: public int x = 1;
Ctor より先に処理される.
using System;
namespace ConsoleApplication28
{
class Test1
{
public int x = 1;
public Test1()
{
Console.WriteLine( x );
}
}
class Test2
{
public int x;
public Test2()
{
Console.WriteLine( x );
x = 2;
Console.WriteLine( x );
}
}
class Test3
{
public int x = 1;
public Test3()
{
Console.WriteLine( x );
x = 2;
Console.WriteLine( x );
}
}
class Class1
{
static void Main(string[] args)
{
Test1 t1 = new Test1();
Test2 t2 = new Test2();
Test3 t3 = new Test3();
}
}
}
クラス内のメンバ変数に直接初期値を指定したサンプル・プログラム5
その疑問に答えるために、もう1つのクラスを用意した。
メンバ変数の初期化とコンストラクタ内での代入の双方を行った場合の例
ここまでのサンプルでは、すべてコンストラクタに public キーワードを付けてきた。
もし、これにprivateキーワードを付けると何が起こるのだろうか。
言い換えれば
これにより、インスタンスが生成できないクラスが出来てしまうわけだが
こんなクラスにも立派な用途がある
using System;
namespace ConsoleApplication31
{
public static void special( string message )
{
Console.WriteLine( message );
}
}
class Class1
{
static void Main(string[] args)
{
// static Method を利用する.
Class2.special("Hello!");
// Class2 t = new Class2(); // エラーになる 'ConsoleApplication31.Class2.Class2()' はアクセスできない保護レベルになっています。
}
}
}
ここで注意すべきことは、
staticなメソッド呼び出しは問題なく実行可能であるが、
newは実行できないことである
コンパイル時に「.Class2.Class2()' はアクセスできない保護レベルになっています。」というエラーが発生する。
コンストラクタにprivateキーワードを付けることによって、
そのクラスからのインスタンス生成を禁止できる。
{
class Class1
{
static void Main(string[] args)
{
// Ctor をよんで, special ( ちょっとわかりずらい. )
Class2.special();
}
}
}
デストラクタ
{
class Class2
{
// Dtor ( 明示的に呼ばないので, Access 修飾なし )
~Class2()
{
Console.WriteLine("Class2's destructor called");
}
}
class Class1
{
static private void test()
{
Console.WriteLine("static private void test() called");
Class2 t1 = new Class2();
Console.WriteLine("static private void test() done");
}
static void Main(string[] args)
{
Console.WriteLine("static void Main(string[] args) called");
test();
Console.WriteLine("static void Main(string[] args) done");
}
}
}
このサンプル・プログラムの場合には Main メソッドの実行後に呼び出されているのが分かる。
Class2クラスのインスタンスは15〜19行目のブロックの内部で宣言された変数t1を経由してしか参照できないので、
メソッドtestの実行が終わればインスタンスは用済みに思える。
これがC++のようなプログラム言語なら
ここでデストラクタを実行させることができるのだが、C#では動作が異なっている。
デストラクタが実行されるのは、メソッドtestの実行が終わり
さらにメソッドMainの実行が終わった後ということになっている
もちろん、必ずこのタイミングでデストラクタが実行される保証はない
確実な終了処理
例外処理を用いて上のサンプルソースを書き直してみたものである。
{
class Class2
{
public void close()
{
Console.WriteLine("public void close() called");
}
}
class Class1
{
{
Console.WriteLine("static void Main(string[] args) called");
test();
Console.WriteLine("static void Main(string[] args) done");
}
}
}
returnでメソッドから直接抜けようが他の例外が発生しようが、どんな理由でも、
tryブロックの実行が終了するときにfinallyブロックは実行される。
20行目の処理がどんな結果に終わろうとも(プロセスそのものが異常終了でもしない限り)必ず24行目は実行される。