はじめに
プロパティで悩んでいる間に、コンストラクタという新しい概念をぶち込まれて手一杯になってきたので、備忘録として残しておく。
誤りについては優しく指摘してくださると嬉しいです。
一言で言うと
- コンストラクタ:インスタンス生成と同時に実行される特殊なメソッドのこと。下に示すように、クラス名と同じ名前で書いて定義して初期値を設定する目的で使われる。1つのクラスの中に複数定義すること (オーバーロード) もできる
コンストラクタを書かずにクラスを書くこともできる。その場合はインスタンス生成のタイミングで、コンパイラが裏で引数なし・処理なしのコンストラクタを生成して実行している。
他にもインスタンス生成のタイミングで、自動で何か処理を行うことにも使われてそうではあるが、具体的な例は調べられていない。
internal class Person
{
private string name = "";
private int age = 0;
public Person() // ← コンストラクタ
{
name = "":
age = 0;
Console.WriteLine("初期値を設定しました");
}
}
// Person p = new Person(); と書くと
// 初期値を設定しました と出力され、初期値がセットされる。
コンストラクタの特徴
戻り値はない
戻り値のintやvoidは書かなくて良い。
引数はあってもなくても良い
引数アリのコンストラクタは以下のように書ける。
internal class Person
{
private string name = "";
private int age = 0;
public Person(string name, int age) // ← 引数アリのコンストラクタ
{
this.age = age; // this.ageはクラス内で定義した方
this.name = name; // 右辺は引数として受け取った方
Console.WriteLine("初期値を設定しました");
}
}
// Person p = new Person("山田太郎", 24) のように書けば
// Console.WriteLine(p.name) で山田太郎と出力される
引数のないものは、thisの部分が書かれて入れば、引数アリのコンストラクタを呼び出してその処理に入る。
this()は「自分のコンストラクタ本体を実行する前に、引数アリのコンストラクタを先に実行せよ」といった意味を持つ
thisの部分が書かれていない場合は、引数アリのコンストラクタは呼ばれない。
以下のように書けば、引数無しのコンストラクタのthisの部分に書いた値をデフォルト値として、その値を使う側が知らなくてもデフォルトのインスタンスを生成できるようになる。
internal class Person
{
private string name = "";
private int age = 0;
public Person() : this("名無し", 0) // 引数ありコンストラクタを呼び出し、引数には「名無し」と0を渡す
{
Console.WriteLine("引数無しコンストラクタが呼ばれました。");
}
public Person(string name, int age)
{
this.name = name;
this.age = age;
Console.WriteLine("引数ありコンストラクタが呼ばれました。 name:{0}, age:{1}", name, age);
}
}
クラスの中に引数アリのコンストラクタを1つでも書くと、クラスの中に引数無しのコンストラクタも書かなければ、インスタンスを生成するときに引数を渡さない形では生成できない。
例えばpublic Person(string name, int age){・・・}のように引数アリの場合のみ書くと、new Person("田中", 30)は動くが、new Person()はエラーになる。
public Person(string name = "名無し", int age = 0) { ... }
のような「デフォルト引数で済むのでは?」とも考えたが、
コンストラクタを使う方が保守性の高いコードが書けるという利点がある。具体的には、ユーザー側が最初からコンストラクタを呼び出すようにしておけば、初期値を変えた時にもキチンと反映される。
反対にデフォルト引数の場合は、初期値を変えてもユーザー側には反映されず、再度コンパイルする必要が出てくる。
他にも理由はあるようなので、理解できたら追記する。
具体例
最後に、Personクラスでコンストラクタを使った書き方とその実行例を示す。
internal class Person
{
private string name = "";
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);
}
public string Name // プロパティ
{
get { return name; }
set { name = value; }
}
public int Age // プロパティ
{
get { return age; }
set { age = value; }
}
}
}
internal class Program
{
static void Main(string[] args)
{
Person p1, p2;
p1 = new Person();
p2 = new Person("太田陸", 29);
p1.Name = "斎藤花子";
p1.Age = 19;
p1.ShowAgeAndName();
p2.ShowAgeAndName();
}
}
}
// 引数アリコンストラクタ name:名無し, age:0 ← 引数アリが先に呼ばれる
// 引数無しコンストラクタ
// 引数アリコンストラクタ name:太田陸, age:29
// 名前:斎藤花子, 年齢:19
// 名前:太田陸, 年齢:29
ファイナライザ
コンストラクタと対になるものとして、ファイナライザがある。こちらはオブジェクトが破棄される際に呼ばれる特殊なメソッド。
クラス名と同じ名前だが先頭に~をつけて定義する。引数は要らず、アクセス修飾子も必要ない。
コンストラクタと違って明示的に呼び出すことはない。
まとめ
コンストラクタはインスタンスを生成した際に呼ばれる特殊なメソッド。クラス名と同じ名前で定義し、初期値の設定やデフォルト状態の設定に使われる。引数はあってもなくても良い。
参考