コンストラクター

クラスの初期化処理

クラスのフィールドは明示的に初期化しない場合、自動的に規定値で初期化されます。
これを明示的に初期化するには宣言時に初期化子を与える方法がありますが、あまり複雑な処理は書けません。
その場合はコンストラクターで初期化します。


private class SimpleClass
{
    //初期化子による初期化
    public string Str1 = "abc";

    //初期化しない
    public string Str2;

    //コンストラクター
    public SimpleClass()
    {
        //コンストラクター内で初期化
        Str2 = "def";
    }
}

static void Main(string[] args)
{
    SimpleClass sc = new SimpleClass();

    Console.WriteLine(sc.Str1);
    Console.WriteLine(sc.Str2);

    Console.ReadLine();
}
abc
def

コンストラクターはクラスのインスタンスを生成する直前に一度だけ呼び出される特殊なメソッドです。
インスタンス生成後にコンストラクターを再度呼び出すことはできません。
コンストラクターの実行が終わるとインスタンスが生成されます。

コンストラクターの定義は

  • クラス名と同じ名前
  • 戻り値を持たない(voidも書かない)

という形式で行います。

そして、基本的にはアクセス修飾子にpublicを指定します。
これを省略するとprivateになるので注意してください。

インスタンス生成時の「new SimpleClass();」というのは、SimpleClassのコンストラクターを呼び出す、という意味になります。
もしコンストラクターがprivateなどで外部からアクセスできないとコンストラクターが呼び出せなくなるので、インスタンスが生成できなくなります。
(あえてインスタンスを生成できないクラスを作る場合もあります)

readonlyフィールドも初期化可能

コンストラクター内ではreadonlyフィールドも初期化することができます。


private class SimpleClass
{
    readonly float tax;

    public SimpleClass()
    {
        //readonlyフィールドも初期化可能
        tax = 1.08f;
    }

    void TestMethod()
    {
        //これはエラー
        tax = 1.08f;
    }
}

既定のコンストラクター

クラスにコンストラクターを定義しない場合、自動的に「何もしないpublicなコンストラクター」が生成されます。
(既定のコンストラクターデフォルトコンストラクターという)
そのためコンストラクターを定義していないクラスでもインスタンスが生成できます。


private class SimpleClass
{
    public string Str1;
}

//↓同じ意味

private class SimpleClass
{
    public string Str1;

    public SimpleClass() {}
}

初期化子で初期化した場合はコンストラクター内で初期化したものとみなされます。


private class SimpleClass
{
    public string Str = "abc";
}

//↓同じ意味

private class SimpleClass
{
    public string Str;

    public SimpleClass()
    {
        Str = "abc";
    }
}

引数付きコンストラクター

コンストラクターはメソッドなので引数を指定することもできます。
引数によって初期化する値を変えることができます。


private class SimpleClass
{
    string firstName;
    string lastName;

    public SimpleClass(string lastName, string firstName)
    {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public void Speak()
    {
        Console.WriteLine("私は{1} {0}です。", firstName, lastName);
    }
}

static void Main(string[] args)
{
    SimpleClass sc1 = new SimpleClass("A山", "B太");
    SimpleClass sc2 = new SimpleClass("C谷", "D子");

    sc1.Speak();
    sc2.Speak();

    Console.ReadLine();
}
私はA山 B太です。
私はC谷 D子です。

デフォルトコンストラクターが生成されないことに注意

このクラスは「引数付きのコンストラクター」のみが定義されているので、引数なしで呼び出せるコンストラクターが存在しません。
コンストラクターがひとつでも定義されている場合はデフォルトコンストラクターは自動生成されません。

つまり、このクラスは引数なしではインスタンスが生成できないということになります。


private class SimpleClass
{
    string firstName;
    string lastName;

    public SimpleClass(string lastName, string firstName)
    {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

static void Main(string[] args)
{
    //エラー
    SimpleClass sc = new SimpleClass();
}

引数なしでもインスタンスを生成したい場合はコンストラクターをオーバーロードして引数なしのコンストラクターを定義します。
コンストラクターの中身は空でも構いませんし、何かの値でフィールドを初期化しても構いません。


private class SimpleClass
{
    string firstName;
    string lastName;

    public SimpleClass()
    {
        firstName = string.Empty;
        lastName = string.Empty;
    }

    public SimpleClass(string lastName, string firstName)
    {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

もしくはデフォルト引数を使用して、引数なしでもコンストラクターを呼び出せるようにします。


private class SimpleClass
{
    string firstName;
    string lastName;

    public SimpleClass(string lastName = "", string firstName = "")
    {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

オブジェクト初期化子

コンストラクターとは直接関係ないのですが、クラスのメンバーを初期化するにはもうひとつ方法があります。
クラス内のpublicメンバーに対してはインスタンス生成時にコンストラクターを介さずに初期化することができます。


private class SimpleClass
{
    public string FirstName;
    public string LastName;

    public SimpleClass()
    {
        FirstName = string.Empty;
        LastName = string.Empty;
    }
}

static void Main(string[] args)
{
    //オブジェクト初期化子で初期化
    SimpleClass sc = new SimpleClass()
    {
        LastName = "A山",
        FirstName = "B太"
    };
}

インタンスの生成に続いてブロック{}を書き、その中にクラスのメンバーに値を代入することで初期化できます。
これをオブジェクト初期化子といいます。

これは以下のコードと同じ意味になります。


SimpleClass sc = new SimpleClass();
sc.LastName = "A山";
sc.FirstName = "B太";

クラスの外部からのアクセスになるので、コンストラクターとは違いpublicなメンバーしか初期化できません。
(その場所からアクセスできるならprivateやprotectedメンバーに対しても可能)

コピーコンストラクター

コピーコンストラクターというのは、自分のクラスのインスタンスのコピーを作るコンストラクターのことです。
C#にはコピーコンストラクターの機能は標準では提供されていませんので自分で記述する必要があります。


private class SimpleClass
{
    string firstName;
    string lastName;

    public SimpleClass()
    {
        firstName = string.Empty;
        lastName = string.Empty;
    }

    public SimpleClass(string lastName, string firstName)
    {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    //コピーコンストラクター
    public SimpleClass(SimpleClass sc)
    {
        firstName = sc.firstName;
        lastName = sc.lastName;
    }
}

static void Main(string[] args)
{
    SimpleClass sc1 = new SimpleClass("A山", "B太");

    //sc1のコピーを得る
    SimpleClass sc2 = new SimpleClass(sc1);
}

コピーコンストラクターは自クラスのインスタンスを引数で受け取ります。
コンストラクター内でフィールドをすべてコピーすれば、インスタンスのコピーが作られます。

ただし参照型のフィールドがある場合はただの代入ではコピーできないので注意が必要です。

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

コンストラクターは別のコンストラクターを呼び出すことができます。


private class SimpleClass
{
    string firstName;
    string lastName;

    public SimpleClass()
        : this("", "")
    {
    }
    //↑は↓のコンストラクターを呼ぶ

    public SimpleClass(string lastName, string firstName)
    {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    //↓は↑のコンストラクターを呼ぶ
    public SimpleClass(SimpleClass sc) 
        : this(sc.lastName, sc.firstName)
    {
    }
}

コンストラクターの引数に続いて「: this (引数リスト)」と書くと、指定の引数リストを持つ別のコンストラクターを呼び出せます。
これをコンストラクター初期化子といいます。

サンプルコードでは、引数なしでコンストラクターを呼び出すとフィールドが空文字で初期化され、自クラスのインスタンスを引数にしてコンストラクターを呼び出すとコピーコンストラクターの働きとなります。
このように、ほとんど同じ処理をするコンストラクターはまとめてしまうとコードが短くなり、修正が容易になります。

別のコンストラクターを呼び出した後に元のコンストラクター内を実行することもできます。


private class SimpleClass
{
    string firstName;
    string lastName;
    int num;

    public SimpleClass()
        : this("", "")
    {
        num = 1;
    }
    //↑は↓のコンストラクターを呼ぶ

    public SimpleClass(string lastName, string firstName)
    {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    //↓は↑のコンストラクターを呼ぶ
    public SimpleClass(SimpleClass sc) 
        : this(sc.lastName, sc.firstName)
    {
        num = 2;
    }
}

このコードのフィールドnumは、引数なしコンストラクターでは1で初期化され、コピーコンストラクターでは2で初期化されます。
引数に文字列を指定してインスタンスを生成した場合は明示的な初期化がありませんので、規定値である0で初期化されます。

なお、コンストラクターの呼び出し順は「コンストラクター初期化子→元のコンストラクター」の順ですので、真ん中のコンストラクターで変数numを初期化しても、呼び出し元のコンストラクター内で再度初期化すればそちらの値となります。

ファイナライザー(デストラクター)

クラスの初期化処理にコンストラクターがあるように、クラスの破棄時の処理にファイナライザー(デストラクター)という処理があります。

C#でのクラスの破棄の大部分はファイナライザーでは行いません。
詳しくはオブジェクトの破棄の項で説明します。