C#のクラスコンストラクタとは?基本文法・初期化・オーバーロードを初心者向けに解説
はじめに
C#でクラスを使い始めると、必ず出てくる重要な仕組みのひとつが「コンストラクタ」です。
コンストラクタは、クラスからインスタンスを作成するときに自動で呼び出される特別な処理です。フィールドやプロパティに初期値を設定したり、必要なデータを受け取って安全な状態のオブジェクトを作ったりするために使います。
初心者のうちは、「メソッドと何が違うのか」「なぜクラス名と同じ名前なのか」「引数なしと引数ありの違いは何か」といった点で混乱しやすいかもしれません。
この記事では、C#のクラスコンストラクタについて、基本文法、初期化、デフォルトコンストラクタ、オーバーロード、継承との関係、よくあるエラーまで初心者向けにわかりやすく解説します。
1. C#のクラスコンストラクタとは?
C#のクラスコンストラクタとは、クラスからインスタンスを作成するときに自動的に実行される特別なメソッドのようなものです。
たとえば、次のようにnewを使ってインスタンスを作成すると、コンストラクタが呼び出されます。
C#Person person = new Person();
このとき、Personクラスに定義されているコンストラクタが自動で実行されます。
1-1. コンストラクタはインスタンス生成時に自動実行される特別なメソッド
コンストラクタは、インスタンスを作成した直後に自動で実行されます。
通常のメソッドは、自分で呼び出す必要があります。
C#person.ShowName();
一方、コンストラクタはnewでインスタンスを作るときに自動的に呼び出されます。
C#Person person = new Person();
つまり、コンストラクタは「インスタンスを作るときの準備処理」を担当する仕組みです。
1-2. 通常のメソッドとの違い
コンストラクタと通常のメソッドには、いくつかの大きな違いがあります。
コンストラクタはクラス名と同じ名前で定義します。また、戻り値の型を書きません。voidも書かない点に注意が必要です。
C#class Person
{
public Person()
{
Console.WriteLine("コンストラクタが呼ばれました");
}
}
通常のメソッドであれば、戻り値の型を書きます。
C#public void ShowMessage()
{
Console.WriteLine("メソッドが呼ばれました");
}
コンストラクタは、インスタンス生成時に自動実行される点でも通常のメソッドとは異なります。
1-3. コンストラクタを使う目的は「初期化」と「安全な状態での生成」
コンストラクタの主な目的は、オブジェクトを初期化することです。
たとえば、Personクラスに名前と年齢を持たせる場合、インスタンスを作った時点で名前や年齢が正しく設定されていると安全です。
C#class Person
{
public string Name { get; }
public int Age { get; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
このようにすると、Personインスタンスを作成するときに必ず名前と年齢を渡す必要があります。
C#Person person = new Person("田中", 25);
必須の値をコンストラクタで受け取ることで、不完全な状態のインスタンスが作られることを防げます。
1-4. 初心者がつまずきやすいコンストラクタの考え方
初心者がつまずきやすいポイントは、コンストラクタを「普通のメソッド」と同じように考えてしまうことです。
コンストラクタはメソッドに似ていますが、目的は処理を呼び出すことではなく、インスタンスを作るときの初期化です。
たとえば、次のような考え方をすると理解しやすくなります。
クラスは設計図、インスタンスは実際に作られたもの、コンストラクタは作るときの初期設定です。
家を建てる例で考えると、クラスは設計図、インスタンスは完成した家、コンストラクタは「住所を決める」「部屋数を決める」「鍵を用意する」といった建築時の初期設定にあたります。
2. C#のクラスとインスタンス生成の基本
コンストラクタを理解するためには、まずクラスとインスタンスの関係を押さえておく必要があります。
2-1. クラスとは設計図、インスタンスとは実体
C#のクラスは、データや処理をまとめた設計図です。
C#class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
このPersonクラスは、「人」を表すための設計図です。しかし、クラスを書いただけでは実際のデータは存在しません。
実際に使えるデータとして作成したものがインスタンスです。
C#Person person = new Person();
このようにnewを使うことで、Personクラスからインスタンスを作成できます。
2-2. new演算子でインスタンスを作成する流れ
C#では、クラスのインスタンスを作成するときにnew演算子を使います。
C#Person person = new Person();
このコードでは、次の流れで処理が行われます。
まず、Personクラスのインスタンスを作るためのメモリが確保されます。次に、フィールドやプロパティの初期化が行われます。その後、コンストラクタが実行されます。最後に、作成されたインスタンスが変数personに代入されます。
初心者のうちは、new Person()の部分でコンストラクタが呼ばれていると覚えるとよいでしょう。
2-3. コンストラクタが呼び出されるタイミング
コンストラクタは、newでインスタンスを作成したタイミングで呼び出されます。
C#class Person
{
public Person()
{
Console.WriteLine("Personが作成されました");
}
}
C#Person person = new Person();
実行すると、次のように表示されます。
C#Personが作成されました
このように、明示的にperson.Person()のように呼び出さなくても、インスタンス作成時に自動実行されます。
2-4. フィールド・プロパティ・メソッドとの関係
クラスには、フィールド、プロパティ、メソッドを定義できます。
フィールドはクラス内部で使うデータ、プロパティは外部から値を取得・設定するための仕組み、メソッドは処理を表します。
C#class Person
{
private string name; // フィールド
public string Name // プロパティ
{
get { return name; }
set { name = value; }
}
public void SayHello() // メソッド
{
Console.WriteLine($"こんにちは、{Name}です");
}
}
コンストラクタは、これらのフィールドやプロパティに初期値を設定するためによく使われます。
C#public Person(string name)
{
Name = name;
}
3. C#コンストラクタの基本文法
C#のコンストラクタには、いくつかの基本ルールがあります。特に「クラス名と同じ名前にする」「戻り値の型を書かない」という点は重要です。
3-1. コンストラクタの書き方
コンストラクタは、クラスの中に次のように定義します。
C#class クラス名
{
アクセス修飾子 クラス名()
{
初期化処理;
}
}
具体例は次のとおりです。
C#class Person
{
public Person()
{
Console.WriteLine("コンストラクタです");
}
}
public Person()の部分がコンストラクタです。
3-2. クラス名と同じ名前で定義するルール
コンストラクタ名は、必ずクラス名と同じにします。
C#class Person
{
public Person()
{
}
}
クラス名がPersonなら、コンストラクタ名もPersonです。
次のようにクラス名と異なる名前にすると、コンストラクタではなく通常のメソッドとして扱われるか、書き方によってはエラーになります。
C#class Person
{
public CreatePerson()
{
}
}
コンストラクタを作るときは、クラス名と完全に一致しているか確認しましょう。
3-3. 戻り値の型を書かない理由
コンストラクタには戻り値の型を書きません。
C#public Person()
{
}
通常のメソッドであればvoidやintなどの戻り値の型を書きます。
C#public void Show()
{
}
しかし、コンストラクタはインスタンス生成時に実行される特別な処理であり、値を返す目的のものではありません。そのため、voidも書きません。
次のように書くと、コンストラクタではなく通常のメソッドになってしまいます。
C#public void Person()
{
}
これは初心者によくあるミスです。
3-4. public・privateなどアクセス修飾子の指定
コンストラクタにはアクセス修飾子を指定できます。
よく使うのはpublicです。
C#public Person()
{
}
publicにすると、クラスの外部からインスタンスを作成できます。
一方、privateにすると、クラスの外部からはインスタンス化できません。
C#class Sample
{
private Sample()
{
}
}
C#Sample sample = new Sample(); // エラー
privateコンストラクタは、インスタンス化を制限したい場合や、ユーティリティクラス、シングルトンパターンなどで使われることがあります。
3-5. 引数なしコンストラクタのサンプルコード
もっとも基本的なコンストラクタは、引数なしコンストラクタです。
C#class Person
{
public string Name { get; set; }
public Person()
{
Name = "未設定";
}
}
このクラスは、インスタンス作成時にNameへ"未設定"を設定します。
C#Person person = new Person();
Console.WriteLine(person.Name);
実行結果は次のようになります。
C#未設定
引数なしコンストラクタは、初期値を固定したい場合や、あとからプロパティを設定したい場合に使われます。
4. コンストラクタで初期化を行う方法
コンストラクタの代表的な使い方は、フィールドやプロパティの初期化です。
初期化とは、インスタンスを使い始める前に必要な値を設定しておくことです。
4-1. フィールドを初期化する基本例
フィールドをコンストラクタで初期化する例を見てみましょう。
C#class Person
{
private string name;
public Person()
{
name = "未設定";
}
public void ShowName()
{
Console.WriteLine(name);
}
}
C#Person person = new Person();
person.ShowName();
実行結果は次のとおりです。
C#未設定
このように、コンストラクタ内でフィールドに値を代入できます。
4-2. プロパティを初期化する基本例
C#では、フィールドよりもプロパティを使う場面が多くあります。
C#class Person
{
public string Name { get; set; }
public Person()
{
Name = "未設定";
}
}
この場合も、インスタンス作成時にNameプロパティへ初期値が設定されます。
C#Person person = new Person();
Console.WriteLine(person.Name);
実行結果は次のようになります。
C#未設定
4-3. 引数を受け取って値を設定するコンストラクタ
コンストラクタには引数を持たせることができます。
C#class Person
{
public string Name { get; set; }
public Person(string name)
{
Name = name;
}
}
このクラスでは、インスタンスを作成するときに名前を渡します。
C#Person person = new Person("佐藤");
Console.WriteLine(person.Name);
実行結果は次のとおりです。
C#佐藤
引数ありコンストラクタを使うと、インスタンスごとに異なる値を設定できます。
4-4. 初期値を固定する場合と外部から渡す場合の使い分け
初期値を固定する場合は、引数なしコンストラクタの中で値を設定するとシンプルです。
C#public Person()
{
Name = "未設定";
}
一方、インスタンスごとに異なる値を設定したい場合は、引数ありコンストラクタを使います。
C#public Person(string name)
{
Name = name;
}
たとえば、すべてのユーザーに同じ初期値を設定したいなら固定値で十分です。しかし、ユーザーごとに名前や年齢が異なる場合は、外部から値を渡すほうが自然です。
4-5. nullや不正な値を防ぐ初期化の考え方
コンストラクタでは、不正な値が入らないようにチェックすることも重要です。
たとえば、名前がnullや空文字では困る場合、次のようにチェックできます。
C#class Person
{
public string Name { get; }
public Person(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("名前は必須です", nameof(name));
}
Name = name;
}
}
年齢が負の値にならないようにすることもできます。
C#class Person
{
public int Age { get; }
public Person(int age)
{
if (age < 0)
{
throw new ArgumentException("年齢は0以上で指定してください", nameof(age));
}
Age = age;
}
}
このように、コンストラクタで値を検証することで、不正な状態のインスタンスを作らせない設計にできます。
5. デフォルトコンストラクタとは?
デフォルトコンストラクタとは、引数を持たないコンストラクタのことです。
C#では、クラスにコンストラクタを1つも書かなかった場合、引数なしのコンストラクタが自動的に用意されます。
5-1. コンストラクタを書かない場合に自動生成される仕組み
次のクラスには、コンストラクタが書かれていません。
C#class Person
{
public string Name { get; set; }
}
しかし、次のようにインスタンスを作成できます。
C#Person person = new Person();
これは、C#が自動的に引数なしのコンストラクタを用意してくれるためです。
イメージとしては、次のようなコンストラクタがあるものとして扱われます。
C#public Person()
{
}
5-2. 自分でコンストラクタを書くとデフォルトコンストラクタはどうなるか
自分でコンストラクタを1つでも定義すると、自動的なデフォルトコンストラクタは生成されません。
C#class Person
{
public string Name { get; set; }
public Person(string name)
{
Name = name;
}
}
この場合、次のコードはエラーになります。
C#Person person = new Person();
なぜなら、Person()という引数なしコンストラクタが存在しないからです。
このクラスのインスタンスを作るには、次のように引数を渡す必要があります。
C#Person person = new Person("田中");
5-3. 引数なしコンストラクタが必要になるケース
引数なしコンストラクタが必要になるケースはいくつかあります。
たとえば、あとからプロパティを設定したい場合です。
C#Person person = new Person();
person.Name = "田中";
また、フレームワークやライブラリによっては、引数なしコンストラクタが必要になることがあります。たとえば、オブジェクトを自動生成してからプロパティに値を設定する仕組みでは、引数なしコンストラクタが求められる場合があります。
そのため、引数ありコンストラクタを定義したうえで、必要に応じて引数なしコンストラクタも明示的に書くことがあります。
C#class Person
{
public string Name { get; set; }
public Person()
{
Name = "未設定";
}
public Person(string name)
{
Name = name;
}
}
5-4. よくあるエラー「引数を指定しないコンストラクタがありません」の原因
「引数を指定しないコンストラクタがありません」というエラーは、引数なしでインスタンスを作ろうとしたのに、クラス側に引数なしコンストラクタが存在しない場合に発生します。
たとえば、次のクラスを考えます。
C#class Person
{
public Person(string name)
{
}
}
この状態で次のように書くとエラーになります。
C#Person person = new Person();
対処法は2つあります。
1つ目は、インスタンス作成時に必要な引数を渡す方法です。
C#Person person = new Person("田中");
2つ目は、クラス側に引数なしコンストラクタを追加する方法です。
C#class Person
{
public Person()
{
}
public Person(string name)
{
}
}
どちらが適切かは、そのクラスをどのように使いたいかによって決まります。
6. コンストラクタのオーバーロード
C#では、コンストラクタを複数定義できます。これをコンストラクタのオーバーロードと呼びます。
6-1. オーバーロードとは同じ名前で引数が異なる定義を作ること
オーバーロードとは、同じ名前で引数の数や型が異なるメソッドやコンストラクタを複数定義することです。
コンストラクタはクラス名と同じ名前なので、複数定義する場合は引数の違いで区別します。
C#class Person
{
public Person()
{
}
public Person(string name)
{
}
public Person(string name, int age)
{
}
}
このように、引数なし、文字列1つ、文字列と整数の2つというように、複数の生成パターンを用意できます。
6-2. 引数なし・引数ありコンストラクタを複数用意する例
次の例では、Personクラスに複数のコンストラクタを用意しています。
C#class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person()
{
Name = "未設定";
Age = 0;
}
public Person(string name)
{
Name = name;
Age = 0;
}
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
このクラスは、次のようにいろいろな方法でインスタンスを作成できます。
C#Person person1 = new Person();
Person person2 = new Person("田中");
Person person3 = new Person("佐藤", 30);
6-3. 引数の数や型で呼び出されるコンストラクタが変わる仕組み
C#は、newで渡された引数の数や型を見て、どのコンストラクタを呼び出すかを判断します。
C#Person person1 = new Person();
これは引数なしコンストラクタを呼び出します。
C#Person person2 = new Person("田中");
これはstring型の引数を1つ持つコンストラクタを呼び出します。
C#Person person3 = new Person("佐藤", 30);
これはstring型とint型の引数を持つコンストラクタを呼び出します。
引数の数や型が一致しない場合は、コンパイルエラーになります。
6-4. this()を使ってコンストラクタ同士を呼び出す方法
コンストラクタの中から別のコンストラクタを呼び出したい場合は、this()を使います。
C#class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person() : this("未設定", 0)
{
}
public Person(string name) : this(name, 0)
{
}
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
この書き方を使うと、初期化処理を1か所にまとめられます。
たとえば、new Person()を呼び出すと、最終的にはPerson(string name, int age)が呼び出され、NameとAgeが設定されます。
同じ初期化処理を複数のコンストラクタに書くと、修正漏れが起きやすくなります。this()を使えば、共通処理をまとめやすくなります。
6-5. オーバーロードを使うメリットと注意点
コンストラクタをオーバーロードすると、インスタンスの作り方を複数用意できます。
たとえば、名前だけ指定したい場合、名前と年齢を指定したい場合、何も指定せず初期値を使いたい場合に対応できます。
ただし、オーバーロードを増やしすぎると、どのコンストラクタを使えばよいのか分かりにくくなります。
C#public Person()
public Person(string name)
public Person(int age)
public Person(string name, int age)
public Person(int age, string name)
このように似たようなコンストラクタが増えると、コードの可読性が下がります。
必要な生成パターンだけを用意し、初期化処理はできるだけ共通化することが大切です。
7. コンストラクタと初期化子の違い
C#では、コンストラクタ以外にもオブジェクト初期化子を使ってプロパティに値を設定できます。
7-1. オブジェクト初期化子とは?
オブジェクト初期化子とは、インスタンス作成時にプロパティへ値を設定する書き方です。
C#Person person = new Person
{
Name = "田中",
Age = 25
};
この書き方では、new Personでインスタンスを作成したあと、NameとAgeに値を設定しています。
クラスは次のように定義できます。
C#class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
7-2. コンストラクタ初期化とオブジェクト初期化子の書き方の違い
コンストラクタを使う場合は、引数として値を渡します。
C#Person person = new Person("田中", 25);
クラス側には、引数を受け取るコンストラクタを定義します。
C#class Person
{
public string Name { get; }
public int Age { get; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
一方、オブジェクト初期化子では、プロパティ名を指定して値を設定します。
C#Person person = new Person
{
Name = "田中",
Age = 25
};
オブジェクト初期化子は見た目がわかりやすく、どのプロパティに何を設定しているかが明確です。
7-3. どちらを使うべきか判断するポイント
コンストラクタを使うべきか、オブジェクト初期化子を使うべきかは、値が必須かどうかで考えるとわかりやすいです。
必ず必要な値はコンストラクタで受け取るのがおすすめです。
C#public Person(string name)
{
Name = name;
}
一方、任意で設定すればよい値はオブジェクト初期化子で扱うと便利です。
C#Person person = new Person("田中")
{
Age = 25
};
このように、必須項目と任意項目を分けると、クラスの使い方がわかりやすくなります。
7-4. 必須項目はコンストラクタ、任意項目は初期化子で扱う考え方
たとえば、ユーザー情報を表すクラスを考えます。
名前は必須、年齢や住所は任意とします。
C#class Person
{
public string Name { get; }
public int Age { get; set; }
public string Address { get; set; }
public Person(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("名前は必須です", nameof(name));
}
Name = name;
}
}
使う側は次のように書けます。
C#Person person = new Person("田中")
{
Age = 25,
Address = "東京都"
};
この設計にすると、名前が未設定のPersonを作ることはできません。一方で、年齢や住所は必要な場合だけ設定できます。
8. 継承とコンストラクタの関係
C#では、クラスを継承すると、親クラスと子クラスのコンストラクタが関係します。
継承を使う場合、派生クラスのインスタンスを作ると、まず基底クラスのコンストラクタが実行され、その後に派生クラスのコンストラクタが実行されます。
8-1. 派生クラスのコンストラクタが呼ばれる流れ
次の例を見てみましょう。
C#class Animal
{
public Animal()
{
Console.WriteLine("Animalのコンストラクタ");
}
}
class Dog : Animal
{
public Dog()
{
Console.WriteLine("Dogのコンストラクタ");
}
}
C#Dog dog = new Dog();
実行結果は次のようになります。
C#Animalのコンストラクタ
Dogのコンストラクタ
派生クラスであるDogを作成していますが、最初に基底クラスのAnimalコンストラクタが呼ばれます。
これは、子クラスのインスタンスには親クラスの部分も含まれているためです。
8-2. base()を使って親クラスのコンストラクタを呼び出す方法
親クラスに引数ありコンストラクタがある場合、子クラスからbase()を使って呼び出します。
C#class Animal
{
public string Name { get; }
public Animal(string name)
{
Name = name;
}
}
class Dog : Animal
{
public Dog(string name) : base(name)
{
}
}
この場合、Dogのコンストラクタで受け取ったnameを、base(name)によってAnimalのコンストラクタへ渡しています。
C#Dog dog = new Dog("ポチ");
Console.WriteLine(dog.Name);
実行結果は次のとおりです。
C#ポチ
8-3. this()とbase()の違い
this()とbase()は、どちらもコンストラクタ呼び出しに使われますが、呼び出し先が異なります。
this()は、同じクラス内の別のコンストラクタを呼び出します。
C#public Person() : this("未設定")
{
}
public Person(string name)
{
Name = name;
}
base()は、親クラスのコンストラクタを呼び出します。
C#public Dog(string name) : base(name)
{
}
同じクラス内で初期化処理を共通化したい場合はthis()、継承元のクラスを初期化したい場合はbase()を使います。
8-4. 継承時によくあるコンストラクタのエラー
継承時によくあるエラーは、親クラスに引数なしコンストラクタがないのに、子クラス側でbase()を指定していないケースです。
C#class Animal
{
public Animal(string name)
{
}
}
class Dog : Animal
{
public Dog()
{
}
}
このコードはエラーになります。Animalには引数なしコンストラクタがないため、Dogのコンストラクタから親クラスを初期化できないからです。
次のようにbase()を使って引数を渡す必要があります。
C#class Dog : Animal
{
public Dog() : base("名前なし")
{
}
}
または、Dogのコンストラクタでも引数を受け取り、それを親クラスへ渡します。
C#class Dog : Animal
{
public Dog(string name) : base(name)
{
}
}
9. staticコンストラクタとprivateコンストラクタ
C#には、通常のインスタンスコンストラクタ以外に、staticコンストラクタやprivateコンストラクタもあります。
9-1. staticコンストラクタとは?
staticコンストラクタは、クラス自体の初期化を行うためのコンストラクタです。
通常のコンストラクタはインスタンスを作るときに実行されますが、staticコンストラクタは静的メンバーが初めて使われる前などに一度だけ実行されます。
C#class AppSettings
{
public static string AppName { get; private set; }
static AppSettings()
{
AppName = "Sample App";
Console.WriteLine("staticコンストラクタが実行されました");
}
}
staticコンストラクタにはアクセス修飾子を書きません。また、引数も指定できません。
9-2. staticコンストラクタが実行されるタイミング
staticコンストラクタは、クラスが初めて使われるタイミングで自動的に実行されます。
C#Console.WriteLine(AppSettings.AppName);
このコードでAppSettingsの静的プロパティにアクセスすると、その前にstaticコンストラクタが実行されます。
staticコンストラクタは通常、静的フィールドや静的プロパティの初期化に使われます。
9-3. privateコンストラクタとは?
privateコンストラクタは、クラスの外部から呼び出せないコンストラクタです。
C#class Utility
{
private Utility()
{
}
public static void PrintMessage()
{
Console.WriteLine("メッセージを表示します");
}
}
このクラスは外部からインスタンス化できません。
C#Utility utility = new Utility(); // エラー
このように、インスタンスを作る必要がないクラスでは、privateコンストラクタを使ってインスタンス化を防ぐことがあります。
9-4. インスタンス化を制限したい場合の使い方
privateコンストラクタは、主にインスタンス化を制限したい場合に使います。
たとえば、静的メソッドだけをまとめたユーティリティクラスでは、インスタンスを作る意味がありません。
C#class MathUtility
{
private MathUtility()
{
}
public static int Add(int a, int b)
{
return a + b;
}
}
このようにしておけば、使う側は次のように静的メソッドだけを利用できます。
C#int result = MathUtility.Add(3, 5);
また、生成できるインスタンスを1つに制限する設計でも、privateコンストラクタが使われることがあります。
10. C#コンストラクタでよくあるエラーと対処法
C#のコンストラクタでは、初心者がつまずきやすいエラーがいくつかあります。ここでは代表的なエラーと対処法を解説します。
10-1. コンストラクタに戻り値の型を書いてしまう
コンストラクタには戻り値の型を書きません。
間違った例です。
C#class Person
{
public void Person()
{
}
}
これはコンストラクタではなく、Personという名前の通常メソッドとして扱われます。
正しくは次のように書きます。
C#class Person
{
public Person()
{
}
}
voidを書かない点に注意しましょう。
10-2. クラス名とコンストラクタ名が一致していない
コンストラクタ名はクラス名と完全に一致している必要があります。
間違った例です。
C#class Person
{
public Human()
{
}
}
正しくは次のように書きます。
C#class Person
{
public Person()
{
}
}
クラス名を変更したときに、コンストラクタ名を変更し忘れることもあるため注意が必要です。
10-3. 引数の数や型が合わない
コンストラクタを呼び出すとき、定義されている引数の数や型と一致していないとエラーになります。
C#class Person
{
public Person(string name, int age)
{
}
}
このクラスに対して、次のように書くとエラーになります。
C#Person person = new Person("田中");
stringとintの2つの引数が必要なのに、stringしか渡していないからです。
正しくは次のように書きます。
C#Person person = new Person("田中", 25);
または、必要に応じて別のコンストラクタを追加します。
C#public Person(string name)
{
}
10-4. 初期化前の値を参照してしまう
コンストラクタ内で、まだ設定していない値を使うと、意図しない結果になることがあります。
C#class Person
{
public string Name { get; set; }
public string Message { get; set; }
public Person(string name)
{
Message = $"こんにちは、{Name}さん";
Name = name;
}
}
この例では、Nameを設定する前にMessageを作っているため、期待した文字列になりません。
正しくは、先にNameを設定します。
C#public Person(string name)
{
Name = name;
Message = $"こんにちは、{Name}さん";
}
コンストラクタ内では、初期化の順番にも注意しましょう。
10-5. アクセス修飾子の指定ミスで呼び出せない
コンストラクタのアクセス修飾子がprivateになっていると、クラスの外部から呼び出せません。
C#class Person
{
private Person()
{
}
}
この場合、次のコードはエラーになります。
C#Person person = new Person();
外部からインスタンスを作成したい場合は、publicにします。
C#class Person
{
public Person()
{
}
}
意図せずprivateにしてしまっていないか確認しましょう。
11. 初心者向け実践サンプル:Personクラスを作って理解する
ここでは、Personクラスを作りながら、コンストラクタの基本を確認します。
名前と年齢を持つクラスを作り、引数なしコンストラクタ、引数ありコンストラクタ、オーバーロードの動きを見ていきましょう。
11-1. 名前と年齢を持つPersonクラスの作成
まず、名前と年齢を持つPersonクラスを作ります。
C#class Person
{
public string Name { get; set; }
public int Age { get; set; }
}
この状態でもインスタンスは作れます。
C#Person person = new Person();
ただし、NameやAgeには明示的な初期値を設定していないため、必要に応じてコンストラクタを追加します。
11-2. 引数なしコンストラクタで初期値を設定する
引数なしコンストラクタを追加して、初期値を設定します。
C#class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person()
{
Name = "未設定";
Age = 0;
}
}
使う側のコードは次のとおりです。
C#Person person = new Person();
Console.WriteLine(person.Name);
Console.WriteLine(person.Age);
実行結果は次のようになります。
C#未設定
0
new Person()を実行した時点で、コンストラクタにより初期値が設定されています。
11-3. 引数ありコンストラクタで任意の値を設定する
次に、名前と年齢を外部から渡せるコンストラクタを作ります。
C#class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
使う側は次のように書きます。
C#Person person = new Person("田中", 25);
Console.WriteLine(person.Name);
Console.WriteLine(person.Age);
実行結果は次のとおりです。
C#田中
25
引数ありコンストラクタを使うと、インスタンス作成時に必要な値を設定できます。
11-4. オーバーロードで複数の生成パターンに対応する
引数なしと引数ありの両方に対応するには、コンストラクタをオーバーロードします。
C#class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person()
{
Name = "未設定";
Age = 0;
}
public Person(string name)
{
Name = name;
Age = 0;
}
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
これで、次の3パターンの生成ができます。
C#Person person1 = new Person();
Person person2 = new Person("田中");
Person person3 = new Person("佐藤", 30);
さらにthis()を使って共通化すると、より保守しやすくなります。
C#class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person() : this("未設定", 0)
{
}
public Person(string name) : this(name, 0)
{
}
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
この書き方なら、実際にNameとAgeを設定する処理は1か所だけになります。
11-5. 実行結果を確認してコンストラクタの動きを理解する
最後に、実行結果を確認してみましょう。
C#class Program
{
static void Main()
{
Person person1 = new Person();
Person person2 = new Person("田中");
Person person3 = new Person("佐藤", 30);
Console.WriteLine($"{person1.Name}:{person1.Age}歳");
Console.WriteLine($"{person2.Name}:{person2.Age}歳");
Console.WriteLine($"{person3.Name}:{person3.Age}歳");
}
}
class Person
{
public string Name { get; set; }
public int Age { get; set; }
public Person() : this("未設定", 0)
{
}
public Person(string name) : this(name, 0)
{
}
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
実行結果は次のようになります。
C#未設定:0歳
田中:0歳
佐藤:30歳
どのコンストラクタが呼ばれるかは、new Person()に渡した引数によって決まります。
12. C#クラスコンストラクタのベストプラクティス
コンストラクタは便利ですが、使い方を間違えると読みづらく保守しにくいコードになります。ここでは、C#のクラスコンストラクタを使うときのベストプラクティスを紹介します。
12-1. 必須データはコンストラクタで受け取る
クラスにとって必須のデータは、コンストラクタで受け取るのがおすすめです。
C#class User
{
public string Email { get; }
public User(string email)
{
if (string.IsNullOrWhiteSpace(email))
{
throw new ArgumentException("メールアドレスは必須です", nameof(email));
}
Email = email;
}
}
このようにすれば、メールアドレスが設定されていないUserインスタンスを作れません。
必須項目をあとから設定する設計にすると、未設定のまま使われるリスクがあります。
12-2. コンストラクタに複雑すぎる処理を書かない
コンストラクタには、基本的に初期化処理を書くべきです。
ファイルの読み込み、ネットワーク通信、重い計算処理などをコンストラクタに詰め込みすぎると、インスタンス作成時の挙動がわかりにくくなります。
C#public Service()
{
// 重い処理を大量に書くのは避けたい
}
複雑な処理が必要な場合は、専用のメソッドやファクトリメソッドに分けることを検討しましょう。
C#public void Initialize()
{
// 必要に応じて初期化処理を実行
}
コンストラクタは、できるだけシンプルに保つことが大切です。
12-3. 不正な状態のインスタンスを作らせない
コンストラクタでは、引数のチェックを行い、不正な状態のインスタンスが作られないようにしましょう。
C#class Product
{
public string Name { get; }
public int Price { get; }
public Product(string name, int price)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("商品名は必須です", nameof(name));
}
if (price < 0)
{
throw new ArgumentException("価格は0以上で指定してください", nameof(price));
}
Name = name;
Price = price;
}
}
このようにすることで、商品名が空だったり、価格がマイナスだったりするインスタンスを防げます。
12-4. オーバーロードが増えすぎた場合の見直し方
コンストラクタのオーバーロードが増えすぎると、クラスの使い方が複雑になります。
C#public User()
public User(string name)
public User(string name, int age)
public User(string name, int age, string address)
public User(string name, int age, string address, string email)
このような状態になると、どのコンストラクタを使うべきか迷いやすくなります。
対策として、必須項目だけをコンストラクタで受け取り、任意項目はプロパティやオブジェクト初期化子で設定する方法があります。
C#User user = new User("田中")
{
Age = 25,
Address = "東京都",
Email = "tanaka@example.com"
};
また、生成処理が複雑な場合は、ファクトリメソッドやビルダーパターンを検討することもあります。
12-5. 読みやすく保守しやすい初期化コードを書くコツ
コンストラクタを読みやすくするには、初期化の役割を明確にすることが大切です。
まず、必須項目をコンストラクタの引数にします。次に、不正な値をチェックします。そして、プロパティやフィールドに値を代入します。
C#public Person(string name, int age)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("名前は必須です", nameof(name));
}
if (age < 0)
{
throw new ArgumentException("年齢は0以上で指定してください", nameof(age));
}
Name = name;
Age = age;
}
このように、チェックと代入の流れを整理すると、あとから読んでも理解しやすいコードになります。
13. C#のクラスコンストラクタに関するよくある質問
ここでは、C#のクラスコンストラクタについて初心者が疑問に感じやすい内容をQ&A形式で解説します。
13-1. コンストラクタは必ず書く必要がありますか?
コンストラクタは必ず書く必要はありません。
クラスにコンストラクタを1つも書かない場合、C#が自動的に引数なしのコンストラクタを用意してくれます。
C#class Person
{
public string Name { get; set; }
}
この場合でも、次のようにインスタンスを作成できます。
C#Person person = new Person();
ただし、初期値を設定したい場合や、必須データを受け取りたい場合は、自分でコンストラクタを定義します。
13-2. コンストラクタは複数作れますか?
はい、コンストラクタは複数作れます。
これをコンストラクタのオーバーロードと呼びます。
C#class Person
{
public Person()
{
}
public Person(string name)
{
}
public Person(string name, int age)
{
}
}
引数の数や型が異なっていれば、複数のコンストラクタを定義できます。
13-3. コンストラクタに戻り値はありますか?
コンストラクタには戻り値の型を書きません。
C#public Person()
{
}
voidも書かない点に注意してください。
C#public void Person()
{
}
このように書くと、コンストラクタではなく通常のメソッドになってしまいます。
13-4. コンストラクタとメソッドの違いは何ですか?
コンストラクタは、インスタンス作成時に自動で呼び出される初期化用の処理です。
一方、メソッドは、必要なタイミングで自分で呼び出す処理です。
C#Person person = new Person(); // コンストラクタが自動で呼ばれる
person.ShowName(); // メソッドを明示的に呼び出す
また、コンストラクタはクラス名と同じ名前で定義し、戻り値の型を書きません。メソッドは任意の名前を付けることができ、戻り値の型を書きます。
13-5. コンストラクタとデストラクタの違いは何ですか?
コンストラクタは、インスタンスを作成するときに実行される処理です。
デストラクタは、オブジェクトが破棄されるときに実行される可能性がある処理です。
C#では、デストラクタは次のように書きます。
C#class Sample
{
~Sample()
{
Console.WriteLine("デストラクタが呼ばれました");
}
}
ただし、C#ではメモリ管理はガベージコレクションによって自動的に行われます。そのため、初心者のうちはデストラクタよりも、コンストラクタによる初期化をしっかり理解することが重要です。
まとめ
C#のクラスコンストラクタは、インスタンス生成時に自動で呼び出される特別な処理です。主な役割は、フィールドやプロパティを初期化し、安全な状態のインスタンスを作ることです。
コンストラクタはクラス名と同じ名前で定義し、戻り値の型を書きません。引数なしコンストラクタを使えば固定の初期値を設定でき、引数ありコンストラクタを使えばインスタンスごとに異なる値を設定できます。
また、コンストラクタはオーバーロードによって複数定義できます。this()を使えば同じクラス内の別コンストラクタを呼び出せるため、初期化処理を共通化できます。継承ではbase()を使って親クラスのコンストラクタを呼び出す点も重要です。
初心者がコンストラクタを理解するうえで大切なのは、「コンストラクタはオブジェクトを安全に使い始めるための初期化処理」と考えることです。
必須データはコンストラクタで受け取り、不正な値はチェックし、複雑すぎる処理は詰め込まないようにすると、読みやすく保守しやすいC#コードを書けるようになります。

