C#コンストラクタとは?初心者向けに基本文法・使い方・初期化の疑問をわかりやすく解説

はじめに

C#を学び始めると、クラスやインスタンスとあわせて「コンストラクタ」という言葉が出てきます。

コンストラクタは、オブジェクトを作成するときに自動的に呼び出される特別な処理です。主に、フィールドやプロパティへ初期値を設定するために使います。

たとえば「ユーザーを作るときは、必ず名前を設定したい」「商品オブジェクトを作るときは、価格が不正な値にならないようにしたい」といった場面で、C#のコンストラクタが役立ちます。

この記事では、C#コンストラクタの基本文法から、引数ありコンストラクタ、デフォルトコンストラクタ、オーバーロード、thisやbaseの使い方、静的コンストラクタまで、初心者向けにわかりやすく解説します。

1. C#のコンストラクタとは?

1-1. コンストラクタの役割は「オブジェクト生成時の初期化」

C#のコンストラクタとは、クラスからインスタンスを作成するときに実行される特別なメソッドのようなものです。

主な役割は、オブジェクト生成時の初期化です。

C#
class User
{
public string Name;

public User()
{
Name = "未設定";
}
}

この例では、Userクラスのインスタンスを作成すると、Name"未設定"が代入されます。

C#
User user = new User();
Console.WriteLine(user.Name); // 未設定

このように、コンストラクタを使うと、インスタンスを作った直後から安全に使える状態にできます。

1-2. 通常のメソッドとの違い

コンストラクタは通常のメソッドと似ていますが、いくつか大きな違いがあります。

通常のメソッドは、好きな名前を付けられ、戻り値を指定できます。

C#
class Sample
{
public void ShowMessage()
{
Console.WriteLine("こんにちは");
}
}

一方、コンストラクタはクラス名と同じ名前にし、戻り値を書きません。

C#
class Sample
{
public Sample()
{
Console.WriteLine("インスタンスが作成されました");
}
}

voidも書かない点に注意しましょう。

C#
public void Sample()
{
}

これはコンストラクタではなく、通常のメソッドとして扱われます。C#ではコンストラクタに戻り値を指定できません。

1-3. コンストラクタが呼び出されるタイミング

コンストラクタは、new演算子でインスタンスを作成したタイミングで呼び出されます。

C#
User user = new User();

このコードを実行すると、Userクラスのコンストラクタが自動的に実行されます。

C#
class User
{
public User()
{
Console.WriteLine("Userが作成されました");
}
}
C#
User user = new User();
// Userが作成されました

自分で user.User() のように呼び出すものではありません。コンストラクタは、インスタンス生成時にC#が自動的に呼び出します。

1-4. 初心者が混同しやすい「クラス」「インスタンス」「初期化」の関係

C#のコンストラクタを理解するには、クラス、インスタンス、初期化の関係を整理しておくとわかりやすくなります。

クラスは設計図です。

C#
class User
{
public string Name;
}

インスタンスは、クラスという設計図から作られた実体です。

C#
User user = new User();

初期化は、作成したインスタンスに必要な値を設定することです。

C#
user.Name = "田中";

コンストラクタを使うと、この初期化処理をインスタンス生成時にまとめて行えます。

C#
class User
{
public string Name;

public User(string name)
{
Name = name;
}
}
C#
User user = new User("田中");

つまり、コンストラクタは「インスタンスを使い始める前に、必要な準備をする仕組み」と考えると理解しやすいです。

2. C#コンストラクタの基本文法

2-1. コンストラクタの書き方

C#のコンストラクタは、次のように書きます。

C#
class クラス名
{
public クラス名()
{
// 初期化処理
}
}

たとえば、Personクラスのコンストラクタは次のようになります。

C#
class Person
{
public Person()
{
Console.WriteLine("Personが作成されました");
}
}

publicはアクセス修飾子です。外部からインスタンスを作成できるようにするため、多くの場合はpublicを付けます。

2-2. コンストラクタ名はクラス名と同じにする

コンストラクタ名は、必ずクラス名と同じにします。

C#
class Product
{
public Product()
{
Console.WriteLine("商品オブジェクトを作成しました");
}
}

クラス名がProductなら、コンストラクタ名もProductです。

次のように名前が違うと、コンストラクタではなく通常のメソッドとして扱われるか、書き方によってはエラーになります。

C#
class Product
{
public CreateProduct()
{
}
}

C#コンストラクタの基本として、「クラス名と同じ名前にする」と覚えておきましょう。

2-3. 戻り値を書かない理由

コンストラクタには戻り値を書きません。voidも書きません。

正しい書き方は次の通りです。

C#
class Product
{
public Product()
{
}
}

間違いやすい書き方は次の通りです。

C#
class Product
{
public void Product()
{
}
}

この場合、Productという名前の通常メソッドになってしまいます。コンストラクタは、インスタンス生成のために使われる特別な処理なので、戻り値を返す目的で作られていません。

2-4. new演算子でコンストラクタを呼び出す流れ

コンストラクタは、new演算子によって呼び出されます。

C#
Product product = new Product();

この処理の流れは次のようになります。

  1. new Product()でメモリ上にオブジェクトを作成する

  2. Productクラスのコンストラクタを実行する

  3. 初期化されたインスタンスを変数productに代入する

実際には複雑な内部処理がありますが、初心者のうちは「newしたときにコンストラクタが呼ばれる」と理解しておけば十分です。

2-5. 基本的なサンプルコードで動きを確認する

次のコードで、C#コンストラクタの動きを確認してみましょう。

C#
using System;

class Program
{
static void Main()
{
Car car = new Car();
Console.WriteLine(car.Name);
}
}

class Car
{
public string Name;

public Car()
{
Name = "未設定の車";
Console.WriteLine("コンストラクタが呼び出されました");
}
}

実行結果は次のようになります。

C#
コンストラクタが呼び出されました
未設定の車

new Car()のタイミングでコンストラクタが呼び出され、その中でNameに値が設定されています。

3. コンストラクタを使うメリット

3-1. インスタンス生成時に必要な値を必ず設定できる

コンストラクタを使う最大のメリットは、インスタンス生成時に必要な値を必ず設定できることです。

たとえば、ユーザーには名前が必須だとします。

C#
class User
{
public string Name;

public User(string name)
{
Name = name;
}
}

このように書くと、Userを作成するときに必ず名前を渡す必要があります。

C#
User user = new User("佐藤");

名前なしで作成しようとするとエラーになります。

C#
User user = new User(); // エラー

これにより、必要な情報がない不完全なインスタンスを作りにくくできます。

3-2. 初期化漏れを防げる

コンストラクタを使わずに、インスタンス作成後に値を設定する方法もあります。

C#
User user = new User();
user.Name = "佐藤";

しかし、この方法では値の設定を忘れる可能性があります。

C#
User user = new User();
// Nameを設定し忘れた

コンストラクタで初期化を強制すれば、このような初期化漏れを防げます。

C#
User user = new User("佐藤");

特に、業務システムやWebアプリケーションでは、未設定の値がバグの原因になることがあります。コンストラクタは、こうした問題を防ぐためにも重要です。

3-3. コードの可読性と保守性が上がる

コンストラクタを使うと、そのクラスを使うために何が必要なのかがわかりやすくなります。

C#
Order order = new Order("A001", 3000);

このコードを見ると、Orderを作るには注文番号と金額が必要だとわかります。

一方で、次のように後から値を設定する形だと、どの値が必須なのか判断しづらくなります。

C#
Order order = new Order();
order.OrderNumber = "A001";
order.Amount = 3000;

コンストラクタを適切に使うことで、クラスの使い方が明確になり、後からコードを読む人にも親切な設計になります。

3-4. オブジェクト指向らしい設計にしやすい

オブジェクト指向では、オブジェクト自身が正しい状態を保つことが重要です。

たとえば、価格がマイナスの商品は本来存在してはいけません。

C#
class Product
{
public string Name { get; }
public int Price { get; }

public Product(string name, int price)
{
if (price < 0)
{
throw new ArgumentException("価格は0以上にしてください");
}

Name = name;
Price = price;
}
}

このようにコンストラクタでチェックすれば、不正な状態のオブジェクトが作られるのを防げます。

C#
Product product = new Product("ノート", -100); // 例外

コンストラクタは、単なる初期化だけでなく、オブジェクトの正しさを守るためにも使えます。

4. 引数ありコンストラクタの使い方

4-1. 引数ありコンストラクタとは

引数ありコンストラクタとは、インスタンス生成時に外部から値を受け取るコンストラクタです。

C#
class User
{
public string Name;

public User(string name)
{
Name = name;
}
}

この場合、new User()ではなく、次のように引数を渡します。

C#
User user = new User("田中");

引数ありコンストラクタを使うと、インスタンスごとに異なる初期値を設定できます。

4-2. フィールドやプロパティに値を代入する方法

コンストラクタで受け取った引数は、フィールドやプロパティに代入して使います。

C#
class User
{
public string Name { get; set; }

public User(string name)
{
Name = name;
}
}
C#
User user = new User("鈴木");
Console.WriteLine(user.Name); // 鈴木

プロパティを使う場合も、基本的な考え方はフィールドと同じです。

読み取り専用のプロパティにすることもできます。

C#
class User
{
public string Name { get; }

public User(string name)
{
Name = name;
}
}

この場合、Nameはコンストラクタ内で設定した後、外部から変更できません。

4-3. 複数の引数を受け取るコンストラクタ

コンストラクタは複数の引数を受け取ることもできます。

C#
class Product
{
public string Name { get; }
public int Price { get; }

public Product(string name, int price)
{
Name = name;
Price = price;
}
}

使う側は、定義された順番で値を渡します。

C#
Product product = new Product("ペン", 120);

Console.WriteLine(product.Name); // ペン
Console.WriteLine(product.Price); // 120

引数が増えすぎると使いづらくなるため、本当に必要な値だけをコンストラクタで受け取るのがポイントです。

4-4. 引数名とメンバー名が同じ場合のthisの使い方

C#では、引数名とフィールド名やプロパティ名を似た名前にすることがよくあります。

C#
class User
{
private string name;

public User(string name)
{
this.name = name;
}
}

このthisは、現在のインスタンス自身を表します。

this.nameはフィールドのname、右側のnameは引数のnameです。

C#
this.name = name;

プロパティの場合も同じように使えます。

C#
class User
{
public string Name { get; }

public User(string name)
{
this.Name = name;
}
}

ただし、プロパティ名を大文字始まり、引数名を小文字始まりにすると、thisを書かなくても区別しやすくなります。

C#
public User(string name)
{
Name = name;
}

4-5. 実践例:ユーザー情報を初期化するクラス

次は、ユーザー情報をコンストラクタで初期化する実践例です。

C#
using System;

class Program
{
static void Main()
{
User user = new User(1, "山田太郎", "yamada@example.com");

Console.WriteLine(user.Id);
Console.WriteLine(user.Name);
Console.WriteLine(user.Email);
}
}

class User
{
public int Id { get; }
public string Name { get; }
public string Email { get; }

public User(int id, string name, string email)
{
Id = id;
Name = name;
Email = email;
}
}

実行結果は次のようになります。

C#
1
山田太郎
yamada@example.com

このように、ユーザー作成時に必要な情報をまとめて渡すことで、インスタンス生成後すぐに使える状態にできます。

5. 引数なしコンストラクタとデフォルトコンストラクタ

5-1. 引数なしコンストラクタとは

引数なしコンストラクタとは、引数を受け取らないコンストラクタです。

C#
class User
{
public string Name { get; set; }

public User()
{
Name = "未設定";
}
}

呼び出すときは、引数を指定しません。

C#
User user = new User();

初期値を固定したい場合や、後からプロパティを設定したい場合に使われます。

5-2. デフォルトコンストラクタが自動生成される条件

C#では、クラスにコンストラクタを1つも定義しなかった場合、引数なしのデフォルトコンストラクタが自動的に用意されます。

C#
class User
{
public string Name { get; set; }
}

このクラスでは、自分でコンストラクタを書いていませんが、次のようにインスタンスを作成できます。

C#
User user = new User();

これは、C#が内部的に引数なしコンストラクタを用意してくれるためです。

5-3. 引数ありコンストラクタを定義するとどうなるか

引数ありコンストラクタを1つでも定義すると、引数なしのデフォルトコンストラクタは自動生成されません。

C#
class User
{
public string Name { get; }

public User(string name)
{
Name = name;
}
}

この場合、次のコードはエラーになります。

C#
User user = new User(); // エラー

正しくは、引数を渡して作成します。

C#
User user = new User("田中");

引数なしでも作成したい場合は、自分で引数なしコンストラクタを定義する必要があります。

C#
class User
{
public string Name { get; }

public User()
{
Name = "未設定";
}

public User(string name)
{
Name = name;
}
}

5-4. 「引数なしコンストラクタが呼べない」原因と対処法

初心者がよく出会うエラーに、「引数なしコンストラクタが呼べない」というものがあります。

原因は、引数ありコンストラクタを定義したことで、デフォルトコンストラクタが自動生成されなくなったことです。

C#
class Product
{
public string Name { get; }

public Product(string name)
{
Name = name;
}
}
C#
Product product = new Product(); // エラー

対処法は2つあります。

1つ目は、正しく引数を渡す方法です。

C#
Product product = new Product("ノート");

2つ目は、引数なしコンストラクタを追加する方法です。

C#
class Product
{
public string Name { get; }

public Product()
{
Name = "未設定";
}

public Product(string name)
{
Name = name;
}
}

「引数ありコンストラクタを定義すると、引数なしコンストラクタは自動では作られない」と覚えておきましょう。

6. コンストラクタのオーバーロード

6-1. コンストラクタのオーバーロードとは

コンストラクタのオーバーロードとは、同じクラス内に複数のコンストラクタを定義することです。

ただし、引数の数や型が異なっている必要があります。

C#
class User
{
public User()
{
}

public User(string name)
{
}

public User(string name, int age)
{
}
}

このように、用途に応じて複数の初期化方法を用意できます。

6-2. 引数の数や型で処理を分ける方法

C#では、呼び出し時に渡した引数に応じて、どのコンストラクタを使うかが決まります。

C#
class Product
{
public string Name { get; }
public int Price { get; }

public Product()
{
Name = "未設定";
Price = 0;
}

public Product(string name)
{
Name = name;
Price = 0;
}

public Product(string name, int price)
{
Name = name;
Price = price;
}
}

使い方は次の通りです。

C#
Product p1 = new Product();
Product p2 = new Product("ペン");
Product p3 = new Product("ノート", 200);

引数なし、文字列1つ、文字列と整数の2つというように、引数の違いで呼び出されるコンストラクタが変わります。

6-3. this()を使って別のコンストラクタを呼び出す

コンストラクタの中から、同じクラスの別のコンストラクタを呼び出したい場合は、this()を使います。

C#
class Product
{
public string Name { get; }
public int Price { get; }

public Product() : this("未設定", 0)
{
}

public Product(string name) : this(name, 0)
{
}

public Product(string name, int price)
{
Name = name;
Price = price;
}
}

この書き方を使うと、初期化処理を1か所にまとめられます。

Product()Product("未設定", 0)を呼び出し、Product(string name)Product(name, 0)を呼び出しています。

6-4. オーバーロードを使うときの注意点

コンストラクタのオーバーロードは便利ですが、増やしすぎると逆にわかりにくくなります。

たとえば、次のように似たようなコンストラクタが多いと、どれを使うべきか迷いやすくなります。

C#
public User(string name)
public User(string email)

どちらもstring型を1つ受け取るため、同じシグネチャになり定義できません。

また、引数の意味がわかりづらい場合もあります。

C#
User user = new User("田中", "taro@example.com", "管理者");

引数が増えすぎる場合は、クラス設計を見直す、プロパティ初期化を使う、専用の設定クラスを作るなどの工夫が必要です。

6-5. サンプルコードで理解するコンストラクタチェーン

this()を使ったコンストラクタチェーンの例を見てみましょう。

C#
using System;

class Program
{
static void Main()
{
Book book1 = new Book();
Book book2 = new Book("C#入門");
Book book3 = new Book("C#実践", 2500);

Console.WriteLine($"{book1.Title} / {book1.Price}");
Console.WriteLine($"{book2.Title} / {book2.Price}");
Console.WriteLine($"{book3.Title} / {book3.Price}");
}
}

class Book
{
public string Title { get; }
public int Price { get; }

public Book() : this("タイトル未設定", 0)
{
}

public Book(string title) : this(title, 1000)
{
}

public Book(string title, int price)
{
Title = title;
Price = price;
}
}

実行結果は次のようになります。

C#
タイトル未設定 / 0
C#入門 / 1000
C#実践 / 2500

最終的な初期化処理は、Book(string title, int price)に集約されています。このように書くと、同じ代入処理を何度も書かずに済みます。

7. プロパティ・フィールドの初期化との違い

7-1. フィールド初期化子との違い

C#では、コンストラクタを使わなくてもフィールドに初期値を設定できます。

C#
class User
{
private string name = "未設定";
}

これをフィールド初期化子と呼びます。

固定の初期値を設定するだけであれば、フィールド初期化子で十分な場合があります。

一方、外部から受け取った値を使って初期化したい場合は、コンストラクタを使います。

C#
class User
{
private string name;

public User(string name)
{
this.name = name;
}
}

7-2. プロパティ初期化子との違い

プロパティにも初期値を設定できます。

C#
class User
{
public string Name { get; set; } = "未設定";
}

このように書くと、Userを作成した時点でNameには"未設定"が入ります。

C#
User user = new User();
Console.WriteLine(user.Name); // 未設定

プロパティ初期化子は、単純な初期値を設定する場合に便利です。

ただし、引数に応じて値を変えたい場合や、バリデーションを行いたい場合はコンストラクタの方が向いています。

7-3. コンストラクタで初期化すべきケース

コンストラクタで初期化すべきなのは、主に次のようなケースです。

必須項目を設定したい場合です。

C#
class Customer
{
public string Name { get; }

public Customer(string name)
{
Name = name;
}
}

引数によって初期値を変えたい場合です。

C#
class TaxCalculator
{
public decimal TaxRate { get; }

public TaxCalculator(decimal taxRate)
{
TaxRate = taxRate;
}
}

不正な値を防ぎたい場合です。

C#
class Item
{
public int Price { get; }

public Item(int price)
{
if (price < 0)
{
throw new ArgumentException("価格は0以上にしてください");
}

Price = price;
}
}

7-4. 初期化子で十分なケース

初期化子で十分なのは、値が固定で、複雑な処理が不要なケースです。

C#
class Settings
{
public int RetryCount { get; set; } = 3;
public string Language { get; set; } = "ja";
}

このような単純な初期値であれば、コンストラクタに書くよりも簡潔です。

C#
class Settings
{
public int RetryCount { get; set; }
public string Language { get; set; }

public Settings()
{
RetryCount = 3;
Language = "ja";
}
}

上のように書いても間違いではありませんが、単純な値なら初期化子の方が読みやすいことが多いです。

7-5. 初心者向けの使い分け基準

初心者のうちは、次の基準で使い分けるとよいでしょう。

固定値を入れるだけなら、フィールド初期化子やプロパティ初期化子を使います。

C#
public string Status { get; set; } = "新規";

インスタンス作成時に必ず値を受け取りたいなら、コンストラクタを使います。

C#
public User(string name)
{
Name = name;
}

値のチェックが必要なら、コンストラクタを使います。

C#
public Product(int price)
{
if (price < 0)
{
throw new ArgumentException("価格は0以上にしてください");
}

Price = price;
}

「固定値なら初期化子」「外から値を受け取るならコンストラクタ」と考えると、最初は理解しやすいです。

8. 継承時のコンストラクタとbaseの使い方

8-1. 親クラスと子クラスのコンストラクタの呼び出し順

C#で継承を使う場合、子クラスのインスタンスを作成すると、まず親クラスのコンストラクタが呼び出され、その後に子クラスのコンストラクタが呼び出されます。

C#
using System;

class Animal
{
public Animal()
{
Console.WriteLine("Animalのコンストラクタ");
}
}

class Dog : Animal
{
public Dog()
{
Console.WriteLine("Dogのコンストラクタ");
}
}
C#
Dog dog = new Dog();

実行結果は次のようになります。

C#
Animalのコンストラクタ
Dogのコンストラクタ

親クラスの初期化が先に行われ、その後に子クラスの初期化が行われます。

8-2. base()で親クラスのコンストラクタを呼び出す

子クラスから親クラスのコンストラクタを明示的に呼び出すには、base()を使います。

C#
class Animal
{
public Animal()
{
Console.WriteLine("Animalを初期化");
}
}

class Dog : Animal
{
public Dog() : base()
{
Console.WriteLine("Dogを初期化");
}
}

base()は、親クラスのコンストラクタを呼び出すための書き方です。

引数なしの親コンストラクタを呼ぶ場合は、明示的に書かなくても自動的に呼び出されます。

C#
public Dog()
{
}

これは、内部的には次のような意味になります。

C#
public Dog() : base()
{
}

8-3. 引数ありの親コンストラクタを呼ぶ方法

親クラスに引数ありコンストラクタしかない場合、子クラスからbase(引数)で呼び出す必要があります。

C#
class Animal
{
public string Name { get; }

public Animal(string name)
{
Name = name;
}
}

class Dog : Animal
{
public string Breed { get; }

public Dog(string name, string breed) : base(name)
{
Breed = breed;
}
}

使う側は次のように書きます。

C#
Dog dog = new Dog("ポチ", "柴犬");

Console.WriteLine(dog.Name); // ポチ
Console.WriteLine(dog.Breed); // 柴犬

nameは親クラスのコンストラクタに渡され、breedは子クラスのコンストラクタで設定されています。

8-4. 継承でよくあるエラーと解決方法

継承時によくあるエラーは、親クラスに引数なしコンストラクタがないのに、子クラス側で親コンストラクタを呼び出していないケースです。

C#
class Animal
{
public Animal(string name)
{
}
}

class Dog : Animal
{
public Dog()
{
}
}

このコードはエラーになります。Animalには引数なしコンストラクタがないため、Dogから呼び出せないからです。

解決方法は、base()で親クラスの引数ありコンストラクタを呼び出すことです。

C#
class Dog : Animal
{
public Dog() : base("名前未設定")
{
}
}

または、子クラスのコンストラクタでも引数を受け取ります。

C#
class Dog : Animal
{
public Dog(string name) : base(name)
{
}
}

継承時は、「親クラスのコンストラクタをどう呼び出すか」を意識しましょう。

9. 静的コンストラクタとは?

9-1. static constructorの役割

静的コンストラクタとは、クラスそのものを初期化するためのコンストラクタです。

通常のコンストラクタはインスタンスを初期化しますが、静的コンストラクタは静的メンバーを初期化するときに使います。

C#
class AppSettings
{
public static string AppName { get; }

static AppSettings()
{
AppName = "Sample App";
}
}

静的コンストラクタにはstaticを付けます。

9-2. 通常のコンストラクタとの違い

通常のコンストラクタは、インスタンスを作成するたびに呼び出されます。

C#
class User
{
public User()
{
Console.WriteLine("通常のコンストラクタ");
}
}
C#
User u1 = new User();
User u2 = new User();

この場合、通常のコンストラクタは2回呼び出されます。

一方、静的コンストラクタは、クラスが初めて使われる前に一度だけ実行されます。

C#
class Counter
{
public static int Value { get; }

static Counter()
{
Value = 100;
Console.WriteLine("静的コンストラクタ");
}
}

また、静的コンストラクタにはアクセス修飾子を付けられません。

C#
static Counter()
{
}

次のようには書けません。

C#
public static Counter()
{
}

9-3. 静的コンストラクタが実行されるタイミング

静的コンストラクタは、静的メンバーに初めてアクセスする前や、インスタンスが初めて作られる前に実行されます。

C#
using System;

class Program
{
static void Main()
{
Console.WriteLine(AppConfig.AppName);
}
}

class AppConfig
{
public static string AppName { get; }

static AppConfig()
{
AppName = "My Application";
Console.WriteLine("静的コンストラクタが実行されました");
}
}

実行結果は次のようになります。

C#
静的コンストラクタが実行されました
My Application

AppConfig.AppNameにアクセスする前に、静的コンストラクタが実行されています。

9-4. 静的コンストラクタを使う場面と注意点

静的コンストラクタは、静的な設定値の初期化や、クラス全体で共有する値の準備に使われます。

C#
class Tax
{
public static decimal Rate { get; }

static Tax()
{
Rate = 0.1m;
}
}

ただし、静的コンストラクタに重い処理を書くと、クラスを初めて使うタイミングで処理が遅くなる可能性があります。

また、静的コンストラクタは自分で直接呼び出せません。

C#
Tax.Tax(); // 呼び出せない

C#が必要なタイミングで自動的に実行するものです。

10. コンストラクタでよくあるエラーと疑問

10-1. 「戻り値を指定できない」と言われる理由

コンストラクタに戻り値を書くと、C#では正しいコンストラクタとして扱われません。

C#
class User
{
public void User()
{
}
}

これはコンストラクタではなく、Userという名前の通常メソッドです。

コンストラクタはインスタンスを初期化するための特別な処理であり、値を返すものではありません。そのため、intstringはもちろん、voidも書きません。

正しい書き方は次の通りです。

C#
class User
{
public User()
{
}
}

10-2. 「コンストラクタが存在しない」と表示される原因

「コンストラクタが存在しない」といったエラーは、呼び出し側の引数と、定義されているコンストラクタの引数が一致していない場合に起こります。

C#
class User
{
public User(string name)
{
}
}

このクラスに対して、次のように呼び出すとエラーになります。

C#
User user = new User();

User(string name)はありますが、User()は定義されていないからです。

対処法は、定義に合わせて引数を渡すことです。

C#
User user = new User("田中");

または、引数なしコンストラクタを追加します。

C#
class User
{
public User()
{
}

public User(string name)
{
}
}

10-3. privateコンストラクタは何に使うのか

コンストラクタにはprivateを付けることもできます。

C#
class Sample
{
private Sample()
{
}
}

privateコンストラクタにすると、クラスの外部からnewできなくなります。

C#
Sample sample = new Sample(); // エラー

privateコンストラクタは、主に外部からインスタンスを作らせたくない場合に使います。

たとえば、静的メソッドだけを持つユーティリティクラスでは、インスタンスを作る必要がありません。

C#
class MathUtility
{
private MathUtility()
{
}

public static int Add(int a, int b)
{
return a + b;
}
}

また、シングルトンパターンなど、インスタンス生成をクラス内部で制御したい場合にも使われます。

10-4. コンストラクタ内で例外を投げてもよいのか

コンストラクタ内で例外を投げることは可能です。

特に、不正な値でインスタンスを作らせたくない場合に使います。

C#
class Product
{
public string Name { get; }
public int Price { get; }

public Product(string name, int price)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("商品名は必須です");
}

if (price < 0)
{
throw new ArgumentException("価格は0以上にしてください");
}

Name = name;
Price = price;
}
}

このようにすると、不正な状態のProductインスタンスが作られるのを防げます。

C#
Product product = new Product("", -100); // 例外

ただし、コンストラクタ内に複雑すぎる処理や時間のかかる処理を書くと、扱いづらくなる場合があります。コンストラクタでは、基本的に初期化と必要最低限のチェックを行うのがおすすめです。

10-5. asyncコンストラクタは使えるのか

C#では、コンストラクタにasyncを付けることはできません。

次のような書き方はできません。

C#
class UserService
{
public async UserService()
{
}
}

コンストラクタは戻り値を持たないため、Taskを返すasyncメソッドとして定義できないからです。

非同期処理が必要な場合は、初期化用の非同期メソッドを別に用意する方法があります。

C#
class UserService
{
public UserService()
{
}

public async Task InitializeAsync()
{
await Task.Delay(1000);
}
}

または、staticメソッドでインスタンス生成と非同期初期化をまとめる方法もあります。

C#
class UserService
{
private UserService()
{
}

public static async Task<UserService> CreateAsync()
{
UserService service = new UserService();
await Task.Delay(1000);
return service;
}
}

使う側は次のように書きます。

C#
UserService service = await UserService.CreateAsync();

非同期の初期化が必要な場合は、コンストラクタではなく、別メソッドやファクトリメソッドを使いましょう。

11. C#コンストラクタの実践的な使い方

11-1. 必須項目をコンストラクタで受け取る設計

実務では、必須項目をコンストラクタで受け取る設計がよく使われます。

C#
class User
{
public string Name { get; }
public string Email { get; }

public User(string name, string email)
{
Name = name;
Email = email;
}
}

このようにすると、Userを作成するには必ずnameemailが必要になります。

C#
User user = new User("田中", "tanaka@example.com");

必須項目をコンストラクタにすることで、不完全なオブジェクトが作られるのを防げます。

11-2. 任意項目はプロパティで設定する設計

すべての値をコンストラクタで受け取る必要はありません。

必須項目はコンストラクタ、任意項目はプロパティで設定する設計もよく使われます。

C#
class User
{
public string Name { get; }
public string Email { get; }

public int Age { get; set; }
public string Address { get; set; } = "";

public User(string name, string email)
{
Name = name;
Email = email;
}
}

使い方は次の通りです。

C#
User user = new User("佐藤", "sato@example.com");
user.Age = 30;
user.Address = "東京都";

必須項目と任意項目を分けることで、コンストラクタの引数が増えすぎるのを防げます。

11-3. 不正な値を防ぐバリデーション

コンストラクタでは、値のチェックも行えます。

C#
class User
{
public string Name { get; }
public string Email { get; }

public User(string name, string email)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("名前は必須です");
}

if (string.IsNullOrWhiteSpace(email))
{
throw new ArgumentException("メールアドレスは必須です");
}

Name = name;
Email = email;
}
}

このように書くと、空の名前やメールアドレスでUserを作成できません。

C#
User user = new User("", ""); // 例外

コンストラクタでバリデーションを行うと、オブジェクトが常に正しい状態になりやすくなります。

11-4. DTOやモデルクラスでの使い方

DTOやモデルクラスでは、シンプルなプロパティ中心の設計にすることも多いです。

C#
class UserDto
{
public int Id { get; set; }
public string Name { get; set; } = "";
public string Email { get; set; } = "";
}

このようなクラスでは、データの受け渡しが目的なので、引数なしコンストラクタとプロパティ初期化を使うことがあります。

一方、ドメインモデルのように「正しい状態を守りたい」クラスでは、コンストラクタで必須項目を受け取る設計が向いています。

C#
class User
{
public int Id { get; }
public string Name { get; }
public string Email { get; }

public User(int id, string name, string email)
{
if (id <= 0)
{
throw new ArgumentException("IDは1以上にしてください");
}

if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("名前は必須です");
}

if (string.IsNullOrWhiteSpace(email))
{
throw new ArgumentException("メールアドレスは必須です");
}

Id = id;
Name = name;
Email = email;
}
}

DTOではシンプルさ、モデルクラスでは正しさを重視すると使い分けやすいです。

11-5. 初心者が真似しやすい実装例

初心者がまず真似しやすいC#コンストラクタの実装例を紹介します。

C#
using System;

class Program
{
static void Main()
{
Product product = new Product("ノート", 150);

Console.WriteLine(product.Name);
Console.WriteLine(product.Price);
}
}

class Product
{
public string Name { get; }
public int Price { get; }

public Product(string name, int price)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("商品名は必須です");
}

if (price < 0)
{
throw new ArgumentException("価格は0以上にしてください");
}

Name = name;
Price = price;
}
}

この例では、商品名と価格をコンストラクタで受け取り、値が不正でないかをチェックしています。

実行結果は次の通りです。

C#
ノート
150

最初はこのように、「必須項目をコンストラクタで受け取る」「不正な値をチェックする」「プロパティに代入する」という流れを覚えるとよいでしょう。

12. C#コンストラクタを理解するための練習問題

12-1. 引数なしコンストラクタを作る練習

まずは、引数なしコンストラクタを作ってみましょう。

次の条件を満たすBookクラスを作成します。

  • Titleプロパティを持つ

  • 引数なしコンストラクタでTitle"未設定"を代入する

  • Mainメソッドでインスタンスを作成して表示する

考え方は、new Book()したときにTitleへ初期値が入るようにすることです。

12-2. 引数ありコンストラクタを作る練習

次に、引数ありコンストラクタを作ってみましょう。

次の条件を満たすStudentクラスを作成します。

  • Nameプロパティを持つ

  • Ageプロパティを持つ

  • コンストラクタでnameageを受け取る

  • 受け取った値をプロパティに代入する

インスタンス作成時には、次のように書けるようにします。

C#
Student student = new Student("田中", 20);

12-3. オーバーロードを使う練習

次は、コンストラクタのオーバーロードです。

次の条件を満たすEmployeeクラスを作成します。

  • Nameプロパティを持つ

  • Departmentプロパティを持つ

  • 引数なしコンストラクタでは、名前を"未設定"、部署を"未所属"にする

  • 引数1つのコンストラクタでは、名前だけを受け取り、部署は"未所属"にする

  • 引数2つのコンストラクタでは、名前と部署を受け取る

余裕があれば、this()を使って初期化処理をまとめてみましょう。

12-4. thisとbaseを使う練習

thisbaseの練習もしてみましょう。

まず、Animalクラスを作成します。

  • Nameプロパティを持つ

  • コンストラクタでnameを受け取る

次に、Dogクラスを作成します。

  • Animalを継承する

  • Breedプロパティを持つ

  • コンストラクタでnamebreedを受け取る

  • namebase(name)で親クラスへ渡す

  • breedは子クラス側で設定する

この練習で、継承時のコンストラクタ呼び出しを確認できます。

12-5. 解答例とコード解説

引数なしコンストラクタの解答例です。

C#
using System;

class Program
{
static void Main()
{
Book book = new Book();
Console.WriteLine(book.Title);
}
}

class Book
{
public string Title { get; }

public Book()
{
Title = "未設定";
}
}

new Book()したときに、引数なしコンストラクタが呼び出され、Title"未設定"が入ります。

次は、引数ありコンストラクタの解答例です。

C#
using System;

class Program
{
static void Main()
{
Student student = new Student("田中", 20);

Console.WriteLine(student.Name);
Console.WriteLine(student.Age);
}
}

class Student
{
public string Name { get; }
public int Age { get; }

public Student(string name, int age)
{
Name = name;
Age = age;
}
}

Studentを作成するときに、名前と年齢を渡しています。

次は、オーバーロードの解答例です。

C#
using System;

class Program
{
static void Main()
{
Employee e1 = new Employee();
Employee e2 = new Employee("佐藤");
Employee e3 = new Employee("鈴木", "営業部");

Console.WriteLine($"{e1.Name} / {e1.Department}");
Console.WriteLine($"{e2.Name} / {e2.Department}");
Console.WriteLine($"{e3.Name} / {e3.Department}");
}
}

class Employee
{
public string Name { get; }
public string Department { get; }

public Employee() : this("未設定", "未所属")
{
}

public Employee(string name) : this(name, "未所属")
{
}

public Employee(string name, string department)
{
Name = name;
Department = department;
}
}

this()を使って、最終的な初期化処理をEmployee(string name, string department)にまとめています。

最後に、thisbaseを使った解答例です。

C#
using System;

class Program
{
static void Main()
{
Dog dog = new Dog("ポチ", "柴犬");

Console.WriteLine(dog.Name);
Console.WriteLine(dog.Breed);
}
}

class Animal
{
public string Name { get; }

public Animal(string name)
{
Name = name;
}
}

class Dog : Animal
{
public string Breed { get; }

public Dog(string name, string breed) : base(name)
{
Breed = breed;
}
}

Dogクラスのコンストラクタでは、base(name)で親クラスAnimalのコンストラクタを呼び出しています。Breedは子クラス側で設定しています。

まとめ

C#コンストラクタは、クラスからインスタンスを作成するときに呼び出される特別な処理です。主な役割は、オブジェクト生成時の初期化です。

コンストラクタ名はクラス名と同じにし、戻り値は書きません。new演算子でインスタンスを作成したタイミングで自動的に呼び出されます。

引数ありコンストラクタを使うと、インスタンス生成時に必要な値を必ず設定できます。これにより、初期化漏れを防ぎ、コードの可読性や保守性を高められます。

また、コンストラクタはオーバーロードできます。this()を使えば同じクラス内の別コンストラクタを呼び出せます。継承時にはbase()を使って親クラスのコンストラクタを呼び出します。

固定値だけならフィールド初期化子やプロパティ初期化子でも十分ですが、外部から値を受け取る場合や、不正な値を防ぎたい場合はコンストラクタが有効です。

C#コンストラクタを理解すると、クラス設計がわかりやすくなり、オブジェクト指向らしい安全なコードを書けるようになります。まずは、引数なしコンストラクタ、引数ありコンストラクタ、オーバーロード、this、baseの使い方から順番に練習していきましょう。