コンストラクターとデストラクター (C# によるプログラミング入門)

++C++; // 未確認飛行 C MVVMパターンを使ったクロス・ターゲット開発 C#たんと学ぶ わりと硬派なソフトウェア開発講座 番外編「Windows Phone 7」

Top総合 目次C# によるプログラミング入門

コンストラクターとデストラクター

このエントリーをはてなブックマークに追加

目次

キーワード

概要

オブジェクトを作成するためには、オブジェクトを正しく初期化してやる必要があります。 そのために、オブジェクトの構築のためのコンストラクターと呼ばれる特殊なメソッドが用意されています。 また、同様にオブジェクトの破棄のためのデストラクターと呼ばれるものもあります。

ポイント
  • コンストラクターで初期化
    • new したときに呼び出される特殊なメソッド。
    • クラス名と同じ名前で定義する。
  • 例えば、class Person { public Person(string name) { ... } ... }

コンストラクター

コンストラクターはインスタンスを正しく初期化するための特別なメソッドです。 コンストラクターは以下のように、クラス名と同じ名前のメソッドを書くことで定義できます。

class SampleClass
{
  // ↓これがコンストラクター
  SampleClass()
  {
    // インスタンスの初期化用のコードを書く
  }
}

他のメソッドと異なり、戻り値の型は書きません(コンストラクターは戻り値を返すことは出来ません)。

例えば、名簿作成のために個人情報を表す Person というクラスを作ったとします。 説明を簡単にするために、この名簿では名前と年齢だけを管理することにします。 そのため、Personnameage という2つのメンバーのみを定義します。

class Person
{
  public string name; // 名前
  public int age;     // 年齢
}

ここで、Personクラスのインスタンスを生成する際、 名前を "" (空の文字列)で、年齢を 0 で初期化したいとします。 そのためには以下のようなコンストラクターを作成します。

class Person
{
  public string name; // 名前
  public int age;     // 年齢

  // ↓これが Person クラスのコンストラクター
  public Person()
  {
    name = "";
    age  = 0;
  }
}

コンストラクターは new を用いてインスタンスを作成する際に呼び出されます。 例えば、下記のようなコードを実行した場合、

using System;

class Test
{
  public Test()
  {
    Console.Write("Test クラスのコンストラクターが呼ばれました\n");
  }
}

class ConstructorSample
{
  static void Main()
  {
    Console.Write("Main の先頭\n");

    Test t = new Test(); // ここで Test のコンストラクターが呼ばれる

    Console.Write("Main の末尾\n");
  }
}

以下のような出力が得られます。

Main の先頭
Sample クラスのコンストラクターが呼ばれました
Main の末尾

また、コンストラクターには引数を与えることもできます。 例えば、先ほどの Person クラスで、 インスタンスの作成時に名前と年齢の値を設定したい場合、 以下のようなコンストラクターを作成します。

class Person
{
  public string name; // 名前
  public int age;     // 年齢

  // ↓引数つきの Person クラスのコンストラクター
  public Person(string name, int age)
  {
    this.name = name;
    this.age  = age;
  }
}

この例で使われている this というキーワードは、 作成するインスタンス自身を格納する特別な変数です。 そのため、この例では this.namePerson クラス内で定義された name のことになります。 一方、this の付いていない方の name は、コンストラクターの引数として定義した name のことです。

引数つきのコンストラクターを呼び出すためには、new を使ってインスタンスを生成する際に、以下のようにして引数を渡します。

クラス名 変数名 = new クラス名(引数リスト);

例えば、先ほど定義したPersonクラスのコンストラクターを呼び出すためには以下のようにします。

Person p = new Person("ビスケット・クルーガー", 57);
Console.Write(p.age); // 57 と表示される

また、コンストラクターはオーバーロードすることができます。 例えば、Person クラスに、名前と年齢を引数として与えるコンストラクターと、何も引数を与えないコンストラクターの両方を定義することができます。

class Person
{
  public string name; // 名前
  public int age;     // 年齢

  // ↓引数なしの Person クラスのコンストラクター
  public Person()
  {
    this.name = "";
    this.age  = 0;
  }

  // ↓引数つきの Person クラスのコンストラクター
  public Person(string name, int age)
  {
    this.name = name;
    this.age  = age;
  }
}

サンプル

using System;

/// <summary>
/// 名簿用の個人情報記録用のクラス。
/// とりあえず、名前と年齢のみ。
/// </summary>
class Person
{
  // public なメンバー変数
  public string name; // 氏名
  public int    age;  // 年齢

  // 定数
  const int UNKNOWN = -1;
  const string DEFAULT_NAME = "デフォルトの名無しさん";

  /// <summary>
  /// 名前と年齢を初期化
  /// 与えられた年齢が負のときは年齢不詳とみなす
  /// </summary>
  /// <param name="name">氏名</param>
  /// <param name="age">年齢</param>
  public Person(string name, int age)
  {
    this.name = name;
    this.age  = age > 0 ? age : UNKNOWN;
  }

  /// <summary>
  /// 名前のみを初期化
  /// 年齢は不詳とする
  /// </summary>
  /// <param name="name">氏名</param>
  public Person(string name) : this(name, UNKNOWN)
  {
  }

  /// <summary>
  /// デフォルトコンストラクター
  /// 氏名・年齢ともに不詳
  /// </summary>
  public Person() : this(null, UNKNOWN)
  {
  }

  /// <summary>
  /// 文字列化
  /// 氏名が不詳のときには NONAME に設定された名前を返す
  /// 年齢が不詳の時には名前のみを返す
  /// 氏名・年齢が分かっているときには「名前(xx歳)」という形の文字列を返す
  /// </summary>
  public override string ToString()
  {
    if(name == null)
      return DEFAULT_NAME;

    if(age == UNKNOWN)
      return name;

    return name + "(" + age + "歳)";
  }
}//class Person

//----------------------------------------------------
// メインプログラム
class ConstructorSample
{
  static void Main()
  {
    Person p1 = new Person("ちゆ", 12);
    Person p2 = new Person("澪");
    Person p3 = new Person();

    Console.Write("{0}\n{1}\n{2}\n", p1, p2, p3);
  }
}
ちゆ(12歳)
澪
デフォルトの名無しさん

コンストラクター初期化子

メンバー変数に初期値を与えるだけなら、 コンストラクターを使わなくても、以下の様な書き方で初期化できます。

class Person
{
    public string name = "";
    public int age = 0;
}

こういう書き方をメンバー変数初期化子(member variable initializer)と言います。

また、以下のような書き方で、別のコンストラクターを呼び出すことができます。

class Person
{
    public string name;
    public int age;

    public Person()
        : this("", 0) // ↓のPerson(string, int) が呼ばれる。
    {
    }

    public Person(string name, int age)
    {
        this.name = name;
        this.age = age;
    }
}

この書き方をコンストラクター初期化子(constructor initializer)と言います。

ちなみに、初期化子とコンストラクターの実行順序は、 メンバー変数初期化子 → コンストラクター初期化子 → コンストラクター本体の順になります。 また、メンバー変数初期化子は、メンバーの宣言順と同じ順序で呼び出されます。

using System;

class Member
{
    public Member(int n)
    {
        Console.Write("メンバー変数初期化子 {0}\n", n);
    }
}

class Test
{
    Member x = new Member(1);
    Member y = new Member(2);

    public Test()
        : this(0)
    {
        Console.Write("引数なしのコンストラクター\n");
    }

    public Test(int x)
    {
        Console.Write("引数 x = {0} 付きのコンストラクター\n", x);
    }
}

class Program
{
    static void Main()
    {
        new Test();
    }
}
メンバー変数初期化子 1
メンバー変数初期化子 2
引数 x = 0 付きのコンストラクター
引数なしのコンストラクター

オブジェクト初期化子

Ver. 3.0

C# 3.0 から、以下のような記法でメンバーを初期化できるようになりました。

Point p = new Point{ X = 0, Y = 1 };

ちなみに、このコードの実行結果は以下のようなコードと等価です。

Point p = new Point();
p.X = 0;
p.Y = 1;

詳細は 「初期化子」 で説明します。

デストラクター

コンストラクターとは逆に、インスタンスが破棄されるときに呼び出されるのがデストラクターです。 デストラクターは以下のように、クラス名の前に ~ を付けた名前のメソッドを書くことで定義できます。

class SampleClass
{
  // ↓これがデストラクター
  ~SampleClass()
  {
    // インスタンスの破棄用のコードを書く
  }
}

デストラクターはコンストラクターと違って、引数を持つことができません。

.NET Framework では、インスタンスの寿命は .NET Framework 自体が管理していて、 いつインスタンスの破棄が行われるのかは分かりません。 (C++ 言語に慣れている人は注意が必要。)

using System;

class Test
{
  public Test()
  {
    Console.Write("Test クラスのコンストラクターが呼ばれました\n");
  }

  ~Test()
  {
    Console.Write("Test クラスのデストラクターが呼ばれました\n");
  }
}

class DestructorSample
{
  static void Main()
  {
    Console.Write("1\n");
    Test t = new Test(); // ここで Test のコンストラクターが呼ばれる
    Console.Write("2\n");
    t = null;            // ↑で作成したインスタンスはもう利用されなくなる
                         // でも、デストラクターはまだ呼ばれない
    Console.Write("3\n");
  }
}
1
Test クラスのコンストラクターが呼ばれました
2
3
Test クラスのデストラクターが呼ばれました

この例では、デストラクターはプログラムの終了時にちゃんと呼び出されていますが、 呼び出されない場合もあります。 このような性質を持っているため、通常、デストラクターはあまり利用されません。

破棄のタイミングを明示的に制御する必要がある場合 (例えば、何らかの外部リソース(ファイルやプリンタなど)の破棄(ファイルのバッファのフラッシュやプリンタの解放)を行う必要がある場合)、 後述する using 文というものを使った Dispose を行います。

演習問題

問題 1

前節クラス問題 1Point 構造体および Triangle クラスに、 以下のようなコンストラクターを追加せよ。

/// <summary>
/// 座標値 (x, y) を与えて初期化。
/// </summary>
/// <param name="x">x 座標値</param>
/// <param name="y">y 座標値</param>
public Point(double x, double y)
/// <summary>
/// 3つの頂点の座標を与えて初期化。
/// </summary>
/// <param name="a">頂点A</param>
/// <param name="b">頂点B</param>
/// <param name="c">頂点C</param>
public Triangle(Point a, Point b, Point c)

解答

[お問い合わせ](q)