コンストラクタ
サンプルプログラム
C# のクラスには、インスタンスを生成する際にただ一度だけ呼び出される、コンストラクタと呼ばれる特殊なメソッドが存在します。以下のサンプルを入力・実行してみてください。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SampleEx101
{
class Person
{
// フィールド name(名前)
private string name = "";
// フィールド age(年齢)
private int age = 0;
// コンストラクタ(引数なし)
public Person() : this("名無し", 0)
{
Console.WriteLine("引数なしコンストラクタ");
}
// コンストラクタ(引数あり)
public Person(string name, int age)
{
this.name = name;
this.age = age;
Console.WriteLine("引数ありコンストラクタ name:{0} age:{1}", name, age);
}
// 情報の表示
public void ShowAgeAndName()
{
Console.WriteLine("名前:{0} 年齢:{1}", name, age);
}
// name のプロパティ
public string Name
{
set { name = value; }
get { return name; }
}
// age のプロパティ
public int Age
{
set { age = value; }
get { return age; }
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SampleEx101
{
class Program
{
static void Main(string[] args)
{
Person p1, p2;
p1 = new Person(); // 引数なしのコンストラクタ
p2 = new Person("太田隆", 29); // 引数ありのコンストラクタ
p1.Name = "斉藤花子";
p1.Age = 18;
p1.ShowAgeAndName();
p2.ShowAgeAndName();
}
}
}
引数なしコンストラクタ
引数ありコンストラクタ name:太田隆 age:29
名前:斉藤花子 年齢:18
名前:太田隆 年齢:29
コンストラクタとは
Person.cs の16行目および21行目を見てください。ここに、戻り値がなく、クラス名と同じ名前のメソッドが定義されています。これがコンストラクタです。コンストラクタはインスタンスが生成されるときに一度だけ呼び出されるメソッドで、インスタンス生成時の初期化処理などをここに記述します。
コンストラクタはこのように一つのクラスで複数定義できます。どのコンストラクタが呼ばれるかは、生成時に与えられた引数に依存します。Program.cs の14行目のように引数がない場合は Person.cs の16行目が、15行目のように引数がある場合は Person.cs の21行目が呼び出されます。
| インスタンス生成 | 呼び出されるコンストラクタ |
|---|---|
p1 = new Person(); | Person()(引数なし) |
p2 = new Person("太田隆", 29); | Person(string name, int age)(引数あり) |
コンストラクタの this による連鎖呼び出し
Person.cs の16行目にある、: の後の this は何を意味するのでしょうか?これは、この引数なしコンストラクタを実行する前に、21行目から定義されている引数付きのコンストラクタを呼び出すという意味です。
第1引数(name)には「名無し」が、第2引数(age)には 0 がそれぞれ与えられます。そのため、引数なしコンストラクタを呼び出すと、まず「引数ありコンストラクタ name:名無し age:0」と表示されてから、「引数なしコンストラクタ」と表示されます(図1-1)。
図1-1. this により、引数付きコンストラクタを先に呼び出す(番号は実行順)
ガーベージコレクション
サンプルプログラム
プログラムの中でインスタンスを大量に生成していくと、その分メモリを圧迫していきます。しかし、すべてのインスタンスが常に必要というわけではありません。C# では、プログラマーが特定のインスタンスを直接消去することはできませんが、代わりにガーベージコレクションという仕組みが存在し、不要なオブジェクトを自動的に整理してくれます。以下のプログラムはガーベージコレクタの使用例です。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SampleEx102
{
class Program
{
static void Main(string[] args)
{
String[] a = new String[10000];
for (int i = 0; i < 10000; i++)
{
a[i] = new String('M', 10000);
}
Console.WriteLine("メモリ使用量(GC発動前) :" + GC.GetTotalMemory(false));
// a の参照を解放
a = null;
Console.WriteLine("メモリ使用量(参照解除後):" + GC.GetTotalMemory(false));
GC.Collect();
Console.WriteLine("メモリ使用量(GC発動後) :" + GC.GetTotalMemory(false));
}
}
}
メモリ使用量(参照解除後):200239724
メモリ使用量(GC発動後) :31312
String クラス
ガーベージコレクタについて説明する前に、13行目で使われている String クラスについて説明します。このクラスはすでに使っている string とまったく同じものです。
String クラスは文字列データの保持および操作を行うためのクラスで、文字列の分割・結合・比較・検索などのメソッドを持ちます。一般に、文字列データを定義する際には小文字の string、クラスとしてメソッドを呼び出す際には String と使い分けられますが、基本的に両者は同じものです。
このサンプルの16行目では以下のような処理を行っています。
ここでは、文字 'M' を10000個連ねた文字列を作っています。さらに、配列変数 a は String 型の10000個の要素を持つ配列変数です。13〜17行目の処理により、長さ10000の文字列が10000個作成されるため、メモリ使用量はかなり大きくなります。しかし、ガーベージコレクションを発動させることでメモリ使用量が一気に下がったことがわかります。
ガーベージコレクションの役割と起動
ガーベージとは英語で「ゴミ」を意味します。つまり、ガーベージコレクションとはメモリの「ゴミ収集」のことです。C# の実行環境では、ガーベージコレクションが自動的に働いてメモリの清掃をしてくれます(図1-2)。
図1-2. ガーベージコレクションのイメージ
ガーベージコレクションがどこで発動されるかはプログラマーには分かりません。しかし、以下の処理でユーザー側から強制的に起動させることもできます(Program.cs 22行目)。
メモリの解放
ガーベージコレクションは、どのようにして使用中のメモリとそうでないメモリを区別するのでしょうか?このプログラムのポイントは20行目にあります。
null とは「何もない」ことを意味します。クラスへの参照を持つ変数に null を代入することで、その変数がいかなるオブジェクトも参照していない状態になります。これにより、13行目で生成された String 型の配列は外部からの参照がなくなり、メモリ内に存在するものの使用されない状態となります。ガーベージコレクションはこのようなデータを自動的に消去します。
メモリ使用量の取得
現在使用されているメモリ量は、以下の処理で調べることができます。
プログラム内では18行目・21行目・23行目でこの処理を行っています。18行目と21行目の結果はほぼ変わりませんが、23行目(ガーベージコレクション後)で値が極端に減少していることが確認できます。
ジェネレーション
new によってインスタンスが生成されるメモリ領域のことをヒープと言います。ヒープに生成されるオブジェクトは、その生存期間に応じて0〜2の3種類に分類されます。この分類をジェネレーションと言います。特定のジェネレーションに対してのみガーベージコレクションを発動させることもできます。
デストラクタ
サンプルプログラム
最後に、コンストラクタとは反対の概念であるデストラクタについて紹介します。デストラクタとは、オブジェクトが破棄されるときに呼び出される特殊なメソッドです。以下のサンプルを実行してみてください。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SampleEx103
{
class Dummy
{
// コンストラクタ
public Dummy()
{
Console.WriteLine("コンストラクタ");
}
// デストラクタ
~Dummy()
{
Console.WriteLine("デストラクタ");
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SampleEx103
{
class Program
{
static void Main(string[] args)
{
Dummy d = new Dummy();
}
}
}
デストラクタ
デストラクタの定義
コンストラクタがクラス名と同じ名前だったのに対し、デストラクタはクラス名の先頭に ~(チルダ)が付きます。
したがって、Dummy クラスのデストラクタは Dummy.cs の17行目のようになります。
コンストラクタとの違いとして、デストラクタは引数を持ちません。また、アクセス修飾子は省略しなければなりません。
デストラクタの呼び出し
デストラクタはオブジェクトが破棄されるときに呼び出されます。コンストラクタと異なり、明示的に呼び出すことはできません。このサンプルでは、Program.cs の Main() メソッドの終了、つまりプログラムが終わるときに呼び出されています。また、プログラム実行中にガーベージコレクションによってオブジェクトが消去される際にも呼び出されます。
IDisposable インターフェースと using ステートメントを使うことが推奨されています。