コンストラクタ

サンプルプログラム

C#のクラスには、インスタンスを生成時にただ一度だけ呼び出される、コンストラクタと呼ばれる特殊なメソッドが存在します。以下、実際にコンストラクタを含んでいるサンプルを紹介します。入力して実行してみてください。

プロジェクト:SampleEx101/ファイル名Person.cs
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;
            }
        }
    }
}
Program.cs
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:0
引数なしコンストラクタ
引数ありコンストラクタ name:太田隆 age:29
名前:斉藤花子 年齢:18
名前:太田隆 年齢:29

コンストラクタ

Person.csの16行目および、21行目を見てください。ここに、戻り値がなく、クラス名と同じメソッドが定義されています。これが、コンストラクタです。コンストラクタは、インスタンスが生成されるときに一度だけ呼び出されるメソッドです。そのため、インスタンス生成時の初期化処理などはここに記述します。

コンストラクタは、このように一つのクラスで複数定義可能です。複数定義されたコンストラクタのうち、どれが呼ばれるかは、生成時に与えられた引数に依存します。たとえば、Program.csの14行目のように、に引数が存在しないケースでは、Person.csの16行目が、Program.csの15行目のようなケースでは、Person.csの21行目が呼び出されます。

Personクラスのコンストラクタとインスタンス生成の対応関係
p1 = new Person();Person()
p2 = new Person("太田隆", 29);Person(string name, int age)

このように、インスタンスを生成する際、引数を与えれば引数付きのコンストラクタが、与えなければ、引数なしのコンストラクタが呼び出されます。ただし、コンストラクタに、該当する形式の引数をとるようなコンストラクタが存在しない場合は、エラーになります

コンストラクタのthis

ところで、Person.csの16行目にある、:の後のthisは何を意味すするのでしょうか?これは、実はこの引数なしのコンストラクタを実行する前に、21行目から定義されている引数付のコンストラクタを呼び出すということを意味しています。

第一の引数(name)には、"名無し"が、第二の引数(age)には、0がそれぞれ与えらえます。そのため、このコンストラクタを呼び出すと、まず「引数ありコンストラクタ name:名無し age:0」と表示されてから、「引数なしコンストラクタ」と表示されるのです。(図1-1.参照)

図1-1.thisにより、引数つきコンストラクタを呼び出す。(番号は実行順)

引数なしコンストラクタと引数つきコンストラクタ

ガーベージコレクション

サンプルプログラム

ところで、プログラムの中で、このようにいくつものインスタンスを生成していくと、その分メモリを圧迫していきます。しかし、すべてのインスタンスが常に必要というわけではありません。そこで、適当なところで、不要なインスタンスを整理する必要があります。

ただ、C#という言語においては、プログラマーが、特定のインスタンスを消去することはできません。代わりに、ガーベージコレクションという仕組みが存在し、時々メモリの中の不要なオブジェクトを整理してくれます。以下のプログラムは、ガーベージクレクタの簡単な使用例です。入力して実行してみてください。

プロジェクト:SampleEx102/ファイル名Program.cs
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));
        }
    }
}
実行結果
メモリ使用量(GC発動前) :200223340
メモリ使用量(参照解除後):200239724
メモリ使用量(GC発動後) :31312

Stringクラス

ガーベージコレクタについて説明する前に、13行目で使われているStringクラスについて説明しましょう。このクラスはC#に用意されているクラスの一つですが、実はすでに使っているstringと全く同じものです。

Stringクラスは文字列のデータの保持および文字列の操作を行うためのクラスで、文字列の分割・結合や比較、検索など文字列を扱うメソッドを使うことができます。

一般に、文字列データを定義する際には小文字から始まるstringクラスとして扱いメソッドを呼び出す際にはStringを使うと使い分けがなされています。

ただ基本的に両者は同じものなので、stringで定義した文字列をStringのメソッドで操作することもできますし、Stringのインスタンスを文字列として扱うことも可能です。このサンプルでは、16行目で以下のような処理を行っています。

Stringクラスのインスタンスの生成

a[i] = new String('M', 10000);

ここでは、String型のインスタンスを生成しています。この際、引数つきコンストラクタを呼び出していますが、その引数として、文字’M’と、整数10000を与えています。これは文字Mを10000個連ねた文字列を作ることを意味しています。更に、配列変数aは、String型の10000個の成分を持つ配列変数です。

そのため、13行目から17行目の処理により、長さ10000の文字列が10000個作成されるわけです。そのため、メモリ使用量はかなり高くなっています。しかし、ガーベージコレクションを発動させることにより、メモリの使用量が一気に下がったことがわかります。

このように、ガーベージコレクションを起動すれば、メモリ内の余計なデータを消去することが可能です。

13行目から17行目の処理で、Stringクラスのインスタンスを大量に生成しています。そのため、メモリ使用量はかなり高くなっています。しかし、ガーベージコレクションを発動させることにより、メモリの使用量が一気に下がったことがわかります。このように、ガーベージコレクションを起動すれば、メモリ内の余計なデータを消去することが可能です。

ガーベージコレクションの役割と起動

ガーベージとは、英語で、ゴミを表します。したがって、ガーベージコレクションとは、いわば、メモリの中の「ゴミ収集車」といったところでしょうか。C#言語の実行環境では、このガーベージコレクションが自動的に働き、メモリの清掃をしてくれる仕組みがあります。(図1-2.参照)

図1-2.ガーベージコレクション

ガーベージコレクション

とはいえ、このガーベージコレクションは、どこで発動されるかは、ユーザーにはわかりません。しかし、ユーザーの側から、ガーベージコレクションを起動させることができます。Program.csの21行目に出ている、以下の処理がそれに当たります。

ガーベージコレクションの起動
GC.Collect();

これにより、ガーベージコレクションが起動され、使用されていないメモリを強制的に排除します。

メモリの解放

では、ガーベージコレクションは、どのようにして使用しているメモリと、そうでないメモリの区別をしているのでしょうか?このプログラムの場合、ポイントは、20行目にあります。

参照にnullを代入
a = null;

nullとは、英語で、「何もない」ということを意味するものです。あるクラスへの参照を代入するべき変数に、この値を代入するということは、その変数が、どんなオブジェクトも参照していない状態にすることを意味します。

これにより、13行目で生成されたString型のサイズ10000の配列は、外部からの参照がなくなることになります。つまり、このオブジェクトは、メモリ内に存在するものの、使用されない状態にあると言うことができます。

基本的にガーベージコレクションは、このようなデータを自動的に消去してくれます、しかし、どのタイミングで呼び出されるかはわかりません。そこで、GC.Collect()によって、強制的にガーベージコレクションを呼び出しているのです。

メモリ容量の取得

なお、以下の処理によって、現在、使用されているメモリがどれだけあるのかを調べることができます。

メモリ使用量の取得
GC.GetTotalMemory(false);

プログラム内では、この処理が、18行目、21行目、23行目で行われていることがわかります。18行目と21行目の処理の結果はあまり変わりませんが、23行目の処理で値が極端に減っていることがわかります。これは、ガーベージコレクションが起動され、不要になったメモリが消去されたことがわかります。

ジェネレーション

ところで、newによってインスタンスなどが生成さえるようメモリ領域のことを、ヒープと言います。ヒープに生成されるオブジェクトは、その生成機関に応じて、3種類に分けられます。この分類の方法を、ジェネレーションと言います。ジェネレーションは、オブジェクトの生存期間の短いものから、0~2に分けられています。また、この値は動的に切り替えられます。以下の方法で、特定のジェネレーションのデータに対してのガーベージコレクションを発動させることができます。

特定のジェネレーションのメモリを消去
GC.Collect(ジェネレーションの数);

デストラクタ

サンプルプログラム

最後に、コンストラクタと反対の概念である、デストラクタについて紹介します。デストラクタとは、コンストラクタの反対で、オブジェクトが破棄されるときに呼び出される特殊なメソッドです。以下のサンプルを実行してみてください。

プロジェクト:SampleEx103/Dummy.cs
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("デストラクタ");
        }
    }
}
ファイル名Program.cs
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行目のようになります。

Dymmyクラスのコンストラクタ
~Dummy()

コンストラクタとの違いは、引数を必要としません。そして、アクセス修飾しは省略しなくてはなりません。

デストラクタの呼び出し

すでに説明したとおり、デストラクタは、オブジェクトが破棄されるときに呼び出されます。そのため、コンストラクタと違い、明示的に呼び出すことができません

このサンプルでも、Program.csのMain()メソッドの終了、つまりプログラムが終わる時に呼び出されています。また、プログラム実行中でも、ガーベージコレクションによってオブジェクトが消去されるときには呼び出されます。

練習問題 : 問題1.