C#コンストラクターとは?基本構文・種類・初期化の使い方を初心者向けにサンプルコードで解説
はじめに
C#でクラスを使い始めると、必ず出てくる重要な仕組みが「コンストラクター」です。英語では「constructor」と書き、検索では「csharp constructor」と調べる人も多いテーマです。
コンストラクターは、オブジェクトを作成するときに最初に実行される特別な処理です。たとえば、ユーザー名、年齢、商品の価格、在庫数など、インスタンスを使う前に必要な値をセットするために使います。
C#のコンストラクターを理解すると、クラスの初期化、オブジェクト指向、継承、static、private、this、baseなどの理解も一気に深まります。
この記事では、C#のコンストラクターについて、基本構文、デフォルトコンストラクター、引数ありコンストラクター、オーバーロード、静的コンストラクター、privateコンストラクター、継承との関係、初期化子との違いまで、初心者向けにサンプルコードを使って解説します。
1. C#のコンストラクターとは?
C#のコンストラクターとは、クラスからインスタンスを生成するときに自動的に呼び出される特別なメンバーです。
クラスは「設計図」、インスタンスは「設計図から作られた実体」とよく説明されます。コンストラクターは、その実体を作るタイミングで必要な初期設定を行う役割を持ちます。
たとえば、次のような場面で使います。
C#public class User
{
public string Name { get; set; }
public User()
{
Name = "Guest";
}
}
この例では、Userクラスのインスタンスを作成すると、Nameに"Guest"が設定されます。
C#User user = new User();
Console.WriteLine(user.Name); // Guest
new User()を実行したときに、User()というコンストラクターが呼び出されています。
1-1. コンストラクターの役割は「インスタンス生成時の初期化」
コンストラクターの主な役割は、インスタンス生成時の初期化です。
初期化とは、オブジェクトを使い始める前に必要な値を準備することです。
たとえば、ユーザー情報を扱うクラスであれば、名前や年齢を最初に設定しておきたいことがあります。
C#public class User
{
public string Name { get; }
public int Age { get; }
public User(string name, int age)
{
Name = name;
Age = age;
}
}
このようにコンストラクターを使うと、インスタンスを作る時点で必要な値を渡せます。
C#User user = new User("Taro", 25);
Console.WriteLine(user.Name); // Taro
Console.WriteLine(user.Age); // 25
コンストラクターを使うことで、「名前が未設定のユーザー」や「年齢が不明なユーザー」のような不完全な状態を避けやすくなります。
1-2. メソッドとの違い:戻り値がなくクラス名と同じ名前を持つ
コンストラクターは見た目がメソッドに似ていますが、通常のメソッドとは違います。
大きな違いは次の2つです。
C#public class Sample
{
public Sample()
{
// これがコンストラクター
}
public void Show()
{
// これは通常のメソッド
}
}
コンストラクターは、クラス名と同じ名前で定義します。上の例では、クラス名がSampleなので、コンストラクター名もSampleです。
また、コンストラクターには戻り値の型を書きません。voidも書きません。
C#public Sample()
{
}
これは正しい書き方です。
一方、次のようにvoidを書くと、コンストラクターではなく通常のメソッドとして扱われます。
C#public void Sample()
{
}
この場合、Sampleという名前のメソッドになってしまい、インスタンス生成時には自動で呼び出されません。初心者がよく間違えるポイントなので注意しましょう。
1-3. コンストラクターが呼び出されるタイミング
コンストラクターは、new演算子でインスタンスを生成したタイミングで呼び出されます。
C#public class Person
{
public Person()
{
Console.WriteLine("コンストラクターが呼び出されました");
}
}
C#Person person = new Person();
実行結果は次のようになります。
コンストラクターが呼び出されました
つまり、new Person()を実行した瞬間に、Person()コンストラクターが呼び出されます。
コンストラクターは、オブジェクトを作るたびに実行されます。
C#Person p1 = new Person();
Person p2 = new Person();
Person p3 = new Person();
この場合、コンストラクターは3回呼び出されます。
1-4. 初心者がつまずきやすい「初期化」と「代入」の違い
コンストラクターを学ぶときに、「初期化」と「代入」の違いで混乱することがあります。
初期化とは、変数やプロパティを最初に使える状態にすることです。
C#public class User
{
public string Name { get; }
public User(string name)
{
Name = name; // 初期化
}
}
この例では、Userオブジェクトが作られるタイミングでNameに値が設定されます。これは初期化です。
一方、代入は、すでに存在する変数やプロパティに値を入れることです。
C#int age;
age = 20;
age = 30;
age = 20;もage = 30;も代入です。
ただし、初期化の中でも代入の記法を使うことがあります。コンストラクター内のName = name;は、文法上は代入ですが、目的としてはインスタンス生成時の初期化です。
特に、読み取り専用プロパティやreadonlyフィールドは、コンストラクターで初期化することが多いです。
C#public class Product
{
public readonly string Code;
public Product(string code)
{
Code = code;
}
}
このように、コンストラクターは「最初に正しい値を入れて、オブジェクトを安全に使える状態にするための仕組み」と考えると理解しやすくなります。
2. C#コンストラクターの基本構文
C#のコンストラクターは、クラスの中に定義します。基本的な書き方はシンプルです。
C#アクセス修飾子 クラス名(引数)
{
初期化処理
}
たとえば、Userクラスのコンストラクターは次のように書きます。
C#public class User
{
public User()
{
Console.WriteLine("Userを作成しました");
}
}
クラス名がUserなので、コンストラクター名もUserです。
2-1. コンストラクターの書き方
最も基本的なコンストラクターは、引数を持たないコンストラクターです。
C#public class Sample
{
public Sample()
{
Console.WriteLine("Sampleクラスのコンストラクターです");
}
}
このコンストラクターは、次のようにインスタンスを作ったときに実行されます。
C#Sample sample = new Sample();
コンストラクターには、public、private、protectedなどのアクセス修飾子を付けられます。
C#public class User
{
public User()
{
}
}
多くの場合、外部からインスタンス化できるようにpublicを使います。
ただし、外部からインスタンス化させたくない場合はprivateコンストラクターを使うこともあります。これは後ほど詳しく解説します。
2-2. new演算子でコンストラクターを呼び出す方法
コンストラクターは、基本的にnew演算子を使って呼び出します。
C#User user = new User();
このコードでは、次の処理が行われます。
C#public class User
{
public User()
{
Console.WriteLine("Userのインスタンスを作成しました");
}
}
実行すると、new User()のタイミングでUser()コンストラクターが呼ばれます。
Userのインスタンスを作成しました
引数ありコンストラクターの場合は、newの中で値を渡します。
C#public class User
{
public string Name { get; }
public User(string name)
{
Name = name;
}
}
C#User user = new User("Hanako");
Console.WriteLine(user.Name); // Hanako
このように、new クラス名(引数)の形でコンストラクターを呼び出します。
2-3. フィールドを初期化する基本サンプルコード
フィールドをコンストラクターで初期化する例を見てみましょう。
C#public class Car
{
private string model;
private int speed;
public Car()
{
model = "Unknown";
speed = 0;
}
public void ShowInfo()
{
Console.WriteLine($"車種: {model}, 速度: {speed}");
}
}
使い方は次のとおりです。
C#Car car = new Car();
car.ShowInfo();
実行結果は次のようになります。
車種: Unknown, 速度: 0
この例では、Carのインスタンスが作られたときに、modelに"Unknown"、speedに0が設定されます。
フィールドに初期値を設定しておくことで、意図しない未設定状態を避けられます。
2-4. プロパティを初期化する基本サンプルコード
C#では、フィールドよりもプロパティを使う場面が多くあります。コンストラクターでは、プロパティも初期化できます。
C#public class Book
{
public string Title { get; set; }
public int Price { get; set; }
public Book()
{
Title = "No Title";
Price = 0;
}
}
使い方は次のとおりです。
C#Book book = new Book();
Console.WriteLine(book.Title); // No Title
Console.WriteLine(book.Price); // 0
引数を使って初期化することもできます。
C#public class Book
{
public string Title { get; set; }
public int Price { get; set; }
public Book(string title, int price)
{
Title = title;
Price = price;
}
}
C#Book book = new Book("C#入門", 2500);
Console.WriteLine(book.Title); // C#入門
Console.WriteLine(book.Price); // 2500
プロパティをコンストラクターで初期化すると、オブジェクト生成時に必要な情報をまとめて設定できます。
2-5. コンストラクターに戻り値を書けない理由
コンストラクターには戻り値を書けません。
次のようなコードは誤りです。
C#public class User
{
public void User()
{
Console.WriteLine("これはコンストラクターではありません");
}
}
voidを書いてしまうと、コンストラクターではなく、Userという名前の通常メソッドになります。
コンストラクターの目的は、値を返すことではなく、インスタンスを初期化することです。
通常のメソッドであれば、次のように戻り値を返します。
C#public string GetName()
{
return "Taro";
}
しかし、コンストラクターはnewによってインスタンス生成の一部として呼び出されます。そのため、戻り値を書く必要がありません。
正しい書き方は次のとおりです。
C#public class User
{
public User()
{
Console.WriteLine("コンストラクターです");
}
}
3. デフォルトコンストラクターの使い方
C#には、デフォルトコンストラクターという仕組みがあります。初心者が混乱しやすいポイントですが、ルールを理解すると簡単です。
3-1. デフォルトコンストラクターとは?
デフォルトコンストラクターとは、引数を持たないコンストラクターのことです。
C#public class User
{
public User()
{
}
}
このUser()がデフォルトコンストラクターです。
次のように、引数なしでインスタンスを作成できます。
C#User user = new User();
デフォルトコンストラクターは、初期値を固定したい場合や、あとからプロパティに値を設定したい場合に使われます。
C#public class User
{
public string Name { get; set; }
public User()
{
Name = "Guest";
}
}
3-2. コンストラクターを定義しない場合に自動生成される仕組み
クラスにコンストラクターを1つも定義しない場合、C#コンパイラは引数なしのコンストラクターを自動的に用意します。
C#public class User
{
public string Name { get; set; }
}
このクラスにはコンストラクターが書かれていません。しかし、次のようにインスタンス化できます。
C#User user = new User();
これは、実質的に次のようなコンストラクターが存在するように扱われるためです。
C#public class User
{
public string Name { get; set; }
public User()
{
}
}
この自動生成される引数なしコンストラクターが、デフォルトコンストラクターです。
3-3. 引数ありコンストラクターを定義すると自動生成されない点に注意
注意したいのは、引数ありコンストラクターを自分で定義すると、引数なしのデフォルトコンストラクターは自動生成されないという点です。
C#public class User
{
public string Name { get; }
public User(string name)
{
Name = name;
}
}
この場合、次のコードはエラーになります。
C#User user = new User(); // エラー
なぜなら、User()という引数なしコンストラクターが存在しないからです。
正しくは、引数を渡して作成します。
C#User user = new User("Taro");
もし引数なしでも作成できるようにしたい場合は、明示的にデフォルトコンストラクターを書きます。
C#public class User
{
public string Name { get; }
public User()
{
Name = "Guest";
}
public User(string name)
{
Name = name;
}
}
これで、次の両方が使えます。
C#User user1 = new User();
User user2 = new User("Taro");
3-4. デフォルトコンストラクターを明示的に書くサンプルコード
デフォルトコンストラクターを明示的に書く例を見てみましょう。
C#public class Product
{
public string Name { get; set; }
public int Price { get; set; }
public Product()
{
Name = "未設定";
Price = 0;
}
}
使い方は次のとおりです。
C#Product product = new Product();
Console.WriteLine(product.Name); // 未設定
Console.WriteLine(product.Price); // 0
このように、引数なしでインスタンス化したときに、初期値を設定できます。
ただし、必ず必要な値がある場合は、デフォルトコンストラクターではなく、引数ありコンストラクターを使ったほうが安全です。
C#public class Product
{
public string Name { get; }
public int Price { get; }
public Product(string name, int price)
{
Name = name;
Price = price;
}
}
この書き方にすると、Productを作るときに必ず商品名と価格を渡す必要があります。
4. 引数ありコンストラクターで初期値を渡す方法
引数ありコンストラクターを使うと、オブジェクト生成時に外部から値を渡して初期化できます。
実務では、デフォルトコンストラクターよりも引数ありコンストラクターを使う場面が多くあります。理由は、必要な値を最初からセットできるため、オブジェクトを安全な状態で作れるからです。
4-1. 引数ありコンストラクターとは?
引数ありコンストラクターとは、コンストラクターに引数を定義したものです。
C#public class User
{
public string Name { get; }
public User(string name)
{
Name = name;
}
}
この例では、Userコンストラクターにstring nameという引数があります。
インスタンス化するときは、次のように値を渡します。
C#User user = new User("Taro");
"Taro"がnameに渡され、コンストラクター内でNameに設定されます。
4-2. オブジェクト生成時に値を渡して初期化するサンプルコード
次の例では、Userクラスに名前と年齢を渡して初期化します。
C#public class User
{
public string Name { get; }
public int Age { get; }
public User(string name, int age)
{
Name = name;
Age = age;
}
public void ShowProfile()
{
Console.WriteLine($"名前: {Name}, 年齢: {Age}");
}
}
使い方は次のとおりです。
C#User user = new User("Hanako", 30);
user.ShowProfile();
実行結果です。
名前: Hanako, 年齢: 30
このように、コンストラクターに引数を持たせることで、インスタンス生成と同時に値を設定できます。
4-3. thisキーワードを使ってフィールドやプロパティに代入する方法
thisキーワードは、現在のインスタンス自身を表します。
次のように、フィールドに値を設定するときによく使われます。
C#public class User
{
private string name;
public User(string name)
{
this.name = name;
}
}
この例では、引数名もname、フィールド名もnameです。
C#this.name = name;
左側のthis.nameはフィールド、右側のnameは引数を表します。
プロパティでも同じように使えます。
C#public class User
{
public string Name { get; }
public User(string name)
{
this.Name = name;
}
}
ただし、プロパティ名がName、引数名がnameのように大文字と小文字で区別できる場合、thisは省略しても問題ありません。
C#public User(string name)
{
Name = name;
}
4-4. 引数名とフィールド名が同じ場合の書き方
引数名とフィールド名が同じ場合、thisを使わないと意図した代入にならないことがあります。
C#public class User
{
private string name;
public User(string name)
{
name = name;
}
}
このコードは一見正しそうですが、実際には引数nameに引数nameを代入しているだけです。フィールドには値が入りません。
正しくは次のように書きます。
C#public class User
{
private string name;
public User(string name)
{
this.name = name;
}
}
this.nameと書くことで、「このインスタンスのフィールド」を明示できます。
実務では、フィールド名にアンダースコアを付ける書き方もよく使われます。
C#public class User
{
private string _name;
public User(string name)
{
_name = name;
}
}
この書き方では、フィールド_nameと引数nameが区別しやすくなります。
4-5. 入力値チェックをコンストラクター内で行うサンプルコード
コンストラクターでは、渡された値が正しいかどうかをチェックできます。
たとえば、ユーザー名が空文字やnullの場合はエラーにしたい場合があります。
C#public class User
{
public string Name { get; }
public int Age { get; }
public User(string name, int age)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("名前は必須です", nameof(name));
}
if (age < 0)
{
throw new ArgumentOutOfRangeException(nameof(age), "年齢は0以上で指定してください");
}
Name = name;
Age = age;
}
}
使い方は次のとおりです。
C#User user = new User("Taro", 25);
不正な値を渡すと例外が発生します。
C#User user = new User("", -1);
コンストラクターで入力値チェックを行うと、不正な状態のオブジェクトが作られるのを防げます。
5. コンストラクターのオーバーロード
C#では、コンストラクターを複数定義できます。これをコンストラクターのオーバーロードと呼びます。
オーバーロードを使うと、インスタンスの作り方を複数用意できます。
5-1. コンストラクターは複数定義できる
次の例では、Userクラスに2つのコンストラクターを定義しています。
C#public class User
{
public string Name { get; }
public int Age { get; }
public User()
{
Name = "Guest";
Age = 0;
}
public User(string name, int age)
{
Name = name;
Age = age;
}
}
このクラスは、次の2通りの方法でインスタンス化できます。
C#User guest = new User();
User user = new User("Taro", 25);
引数なしの場合はGuest、引数ありの場合は渡した値で初期化されます。
5-2. 引数の数や型で処理を分ける方法
コンストラクターのオーバーロードでは、引数の数や型を変える必要があります。
C#public class Product
{
public string Name { get; }
public int Price { get; }
public int Stock { get; }
public Product(string name)
{
Name = name;
Price = 0;
Stock = 0;
}
public Product(string name, int price)
{
Name = name;
Price = price;
Stock = 0;
}
public Product(string name, int price, int stock)
{
Name = name;
Price = price;
Stock = stock;
}
}
使い方は次のとおりです。
C#Product p1 = new Product("Pen");
Product p2 = new Product("Notebook", 500);
Product p3 = new Product("Bag", 3000, 10);
このように、引数の数が違うことで、どのコンストラクターを呼ぶかをC#が判断します。
また、引数の型が違っていれば、同じ数の引数でもオーバーロードできます。
C#public class Sample
{
public Sample(string text)
{
}
public Sample(int number)
{
}
}
5-3. this()を使って別のコンストラクターを呼び出す方法
コンストラクターから、同じクラス内の別のコンストラクターを呼び出すにはthis()を使います。
C#public class User
{
public string Name { get; }
public int Age { get; }
public User() : this("Guest", 0)
{
}
public User(string name, int age)
{
Name = name;
Age = age;
}
}
この例では、引数なしコンストラクターが、引数ありコンストラクターを呼び出しています。
C#User user = new User();
このコードを実行すると、内部的には次のように初期化されます。
C#Name = "Guest";
Age = 0;
this()を使うことで、初期化処理を1か所にまとめられます。
5-4. 重複コードを減らすコンストラクターチェーンの書き方
複数のコンストラクターに同じ初期化処理を書くと、コードが重複します。
たとえば、次のコードは重複が多い例です。
C#public class Product
{
public string Name { get; }
public int Price { get; }
public int Stock { get; }
public Product(string name)
{
Name = name;
Price = 0;
Stock = 0;
}
public Product(string name, int price)
{
Name = name;
Price = price;
Stock = 0;
}
public Product(string name, int price, int stock)
{
Name = name;
Price = price;
Stock = stock;
}
}
これをthis()で書き直すと、次のようになります。
C#public class Product
{
public string Name { get; }
public int Price { get; }
public int Stock { get; }
public Product(string name) : this(name, 0, 0)
{
}
public Product(string name, int price) : this(name, price, 0)
{
}
public Product(string name, int price, int stock)
{
Name = name;
Price = price;
Stock = stock;
}
}
最終的な初期化処理は、引数が3つのコンストラクターに集約されています。
このように、あるコンストラクターから別のコンストラクターを呼び出す書き方をコンストラクターチェーンと呼びます。
5-5. オーバーロードを使うときの注意点
コンストラクターのオーバーロードは便利ですが、増やしすぎるとコードが読みにくくなります。
たとえば、次のように似たようなコンストラクターが多いと、どれを使えばよいか分かりにくくなります。
C#public Product(string name) { }
public Product(string name, int price) { }
public Product(string name, int stock) { } // 同じ型の組み合わせは不可
特に、同じ型の引数が並ぶ場合は注意が必要です。
C#public class User
{
public User(string firstName, string lastName)
{
}
}
この場合、呼び出し側で順番を間違える可能性があります。
C#User user = new User("Yamada", "Taro");
どちらが姓でどちらが名なのか、コードだけでは分かりにくい場合があります。
オーバーロードを使うときは、次の点を意識しましょう。
必要以上にコンストラクターを増やさない
共通処理は
this()でまとめる引数の意味が分かる名前にする
引数が多すぎる場合は別の設計を考える
6. C#コンストラクターの主な種類
C#のコンストラクターには、いくつかの種類があります。基本のインスタンスコンストラクターだけでなく、静的コンストラクターやprivateコンストラクターなどもあります。
ここでは、C#でよく使われるコンストラクターの種類を整理します。
6-1. インスタンスコンストラクター
インスタンスコンストラクターは、クラスのインスタンスを作成するときに呼び出される通常のコンストラクターです。
C#public class User
{
public string Name { get; }
public User(string name)
{
Name = name;
}
}
使い方は次のとおりです。
C#User user = new User("Taro");
new User("Taro")によって、Userクラスのインスタンスが作成され、コンストラクターでNameが初期化されます。
通常、単に「コンストラクター」と言う場合は、このインスタンスコンストラクターを指すことが多いです。
6-2. 静的コンストラクター
静的コンストラクターは、クラスそのものを初期化するためのコンストラクターです。
C#public class AppSettings
{
public static string AppName { get; }
static AppSettings()
{
AppName = "Sample App";
}
}
静的コンストラクターは、インスタンスを作るたびに呼ばれるのではなく、クラスが初めて使われるタイミングで一度だけ実行されます。
C#Console.WriteLine(AppSettings.AppName);
静的フィールドや静的プロパティの初期化に使われます。
6-3. privateコンストラクター
privateコンストラクターは、クラスの外部からインスタンス化できないようにするコンストラクターです。
C#public class Utility
{
private Utility()
{
}
public static void Log(string message)
{
Console.WriteLine(message);
}
}
このクラスは、外部から次のようにインスタンス化できません。
C#Utility utility = new Utility(); // エラー
privateコンストラクターは、ユーティリティクラスやシングルトンパターンなどで使われます。
6-4. コピーコンストラクター風の実装
C#にはC++のような言語仕様としてのコピーコンストラクターはありませんが、似たような実装はできます。
C#public class User
{
public string Name { get; }
public int Age { get; }
public User(string name, int age)
{
Name = name;
Age = age;
}
public User(User other)
{
Name = other.Name;
Age = other.Age;
}
}
使い方は次のとおりです。
C#User user1 = new User("Taro", 25);
User user2 = new User(user1);
Console.WriteLine(user2.Name); // Taro
Console.WriteLine(user2.Age); // 25
このように、同じ型のオブジェクトを引数に受け取り、その値を使って新しいオブジェクトを作ることができます。
6-5. レコードやクラスで使えるプライマリコンストラクター
C#では、レコードでプライマリコンストラクターをよく使います。
C#public record User(string Name, int Age);
このコードでは、NameとAgeを持つUserレコードを簡潔に定義できます。
C#User user = new User("Taro", 25);
Console.WriteLine(user.Name);
Console.WriteLine(user.Age);
また、近年のC#ではクラスでもプライマリコンストラクターを使えるようになっています。
C#public class User(string name, int age)
{
public string Name { get; } = name;
public int Age { get; } = age;
}
通常のコンストラクターよりも短く書けるため便利ですが、初心者のうちは通常のコンストラクターから理解するのがおすすめです。
7. 静的コンストラクターの使い方
静的コンストラクターは、インスタンスではなくクラス自体の初期化に使います。
通常のコンストラクターとは呼び出されるタイミングや書き方が異なるため、違いをしっかり理解しておきましょう。
7-1. staticコンストラクターとは?
staticコンストラクターとは、クラスが初めて使われる前に一度だけ実行されるコンストラクターです。
C#public class Sample
{
static Sample()
{
Console.WriteLine("静的コンストラクターが呼び出されました");
}
}
通常のコンストラクターはインスタンスを作るたびに呼び出されますが、静的コンストラクターはクラス単位で一度だけ呼び出されます。
静的コンストラクターは、次のような用途で使います。
staticフィールドの初期化
初回利用時に一度だけ行いたい処理
クラス全体で共有する設定の準備
7-2. クラスが初めて使われるタイミングで一度だけ実行される
静的コンストラクターは、クラスが初めて使われるタイミングで実行されます。
C#public class Counter
{
public static int Count { get; }
static Counter()
{
Console.WriteLine("Counterの静的コンストラクター");
Count = 100;
}
}
次のコードを実行します。
C#Console.WriteLine(Counter.Count);
Console.WriteLine(Counter.Count);
実行結果は次のようになります。
Counterの静的コンストラクター
100
100
Counter.Countを2回参照していますが、静的コンストラクターは1回だけ実行されます。
7-3. staticフィールドの初期化サンプルコード
静的コンストラクターは、staticフィールドの初期化に使えます。
C#public class TaxCalculator
{
public static readonly decimal TaxRate;
static TaxCalculator()
{
TaxRate = 0.10m;
}
public static decimal Calculate(decimal price)
{
return price * (1 + TaxRate);
}
}
使い方は次のとおりです。
C#decimal total = TaxCalculator.Calculate(1000);
Console.WriteLine(total); // 1100.00
この例では、TaxRateを静的コンストラクターで初期化しています。
ただし、単純な値であれば、フィールド宣言時に初期化するほうが読みやすい場合もあります。
C#public static readonly decimal TaxRate = 0.10m;
静的コンストラクターは、複雑な初期化が必要な場合に使うとよいでしょう。
7-4. 静的コンストラクターに引数やアクセス修飾子を書けない理由
静的コンストラクターには、引数を書けません。
C#static Sample(string name) // エラー
{
}
また、publicやprivateなどのアクセス修飾子も書けません。
C#public static Sample() // エラー
{
}
正しい書き方は次のとおりです。
C#static Sample()
{
}
静的コンストラクターは、プログラム側がnewで明示的に呼び出すものではありません。C#の実行環境が適切なタイミングで自動的に呼び出します。
そのため、外部からアクセスするためのpublicや、値を渡すための引数は必要ありません。
7-5. 通常のコンストラクターとの違い
通常のコンストラクターと静的コンストラクターの違いを整理すると、次のようになります。
| 項目 | 通常のコンストラクター | 静的コンストラクター |
|---|---|---|
| 対象 | インスタンス | クラス |
| 呼び出し回数 | インスタンス生成ごと | クラスごとに一度 |
| 引数 | 指定できる | 指定できない |
| アクセス修飾子 | 指定できる | 指定できない |
| 主な用途 | インスタンスの初期化 | staticメンバーの初期化 |
通常のコンストラクターは、newでインスタンスを作るときに使います。
C#User user = new User();
静的コンストラクターは、クラスが初めて使われる前に自動で実行されます。
C#Console.WriteLine(AppSettings.AppName);
8. privateコンストラクターの使い方
privateコンストラクターは、外部からnewできないようにするためのコンストラクターです。
通常のクラスは、publicコンストラクターを持つことで外部からインスタンス化できます。しかし、あえて外部からインスタンス化させたくない場合もあります。
8-1. privateコンストラクターとは?
privateコンストラクターとは、アクセス修飾子にprivateを指定したコンストラクターです。
C#public class Sample
{
private Sample()
{
}
}
このように書くと、クラスの外部からはコンストラクターを呼び出せません。
C#Sample sample = new Sample(); // エラー
privateはクラス内部からのみアクセスできるという意味です。そのため、privateコンストラクターはクラスの外側からインスタンス化させないために使います。
8-2. 外部からインスタンス化させたくない場合に使う
privateコンストラクターは、次のようなケースで使われます。
staticメソッドだけを持つユーティリティクラス
インスタンスを1つだけに制限したいシングルトン
ファクトリーメソッド経由でインスタンスを作らせたいクラス
たとえば、ユーティリティクラスでは、インスタンス化する必要がないことがあります。
C#public class MathUtility
{
private MathUtility()
{
}
public static int Square(int number)
{
return number * number;
}
}
使い方は次のとおりです。
C#int result = MathUtility.Square(5);
Console.WriteLine(result); // 25
このクラスはstaticメソッドを使うだけなので、インスタンスを作る必要がありません。
8-3. シングルトンパターンで使うサンプルコード
privateコンストラクターは、シングルトンパターンでよく使われます。
シングルトンパターンとは、インスタンスを1つだけ作り、それを共有する設計パターンです。
C#public class AppConfig
{
private static readonly AppConfig _instance = new AppConfig();
public static AppConfig Instance
{
get { return _instance; }
}
public string AppName { get; }
private AppConfig()
{
AppName = "My Application";
}
}
使い方は次のとおりです。
C#AppConfig config = AppConfig.Instance;
Console.WriteLine(config.AppName);
外部からは次のように書けません。
C#AppConfig config = new AppConfig(); // エラー
コンストラクターがprivateなので、外部から自由にインスタンスを作ることを防げます。
8-4. staticクラスとの違い
privateコンストラクターを持つクラスとstaticクラスは似ていますが、違いがあります。
staticクラスは、インスタンス化できず、すべてのメンバーがstaticである必要があります。
C#public static class LogUtility
{
public static void Log(string message)
{
Console.WriteLine(message);
}
}
一方、privateコンストラクターを持つ通常クラスは、クラス内部ではインスタンスを作れます。
C#public class AppConfig
{
private static readonly AppConfig _instance = new AppConfig();
public static AppConfig Instance => _instance;
private AppConfig()
{
}
}
使い分けの目安は次のとおりです。
| 使い方 | 適した設計 |
|---|---|
| 単なる便利メソッドの集まり | staticクラス |
| インスタンスを1つだけ持ちたい | privateコンストラクター + シングルトン |
| 生成方法を制御したい | privateコンストラクター + ファクトリーメソッド |
8-5. privateコンストラクターを使うときの注意点
privateコンストラクターを使うと、外部からインスタンス化できなくなります。
そのため、インスタンスが必要なクラスに安易にprivateコンストラクターを付けると、使いにくいクラスになります。
C#public class User
{
private User()
{
}
}
このようにしてしまうと、外部からUserを作れません。
C#User user = new User(); // エラー
privateコンストラクターを使うときは、代わりにインスタンスを取得する方法を用意する必要があります。
C#public class User
{
public string Name { get; }
private User(string name)
{
Name = name;
}
public static User Create(string name)
{
return new User(name);
}
}
使い方は次のとおりです。
C#User user = User.Create("Taro");
このように、privateコンストラクターは「インスタンス化を禁止する」だけでなく、「インスタンス化の方法を制御する」ためにも使えます。
9. 継承とコンストラクター
C#では、クラスを継承すると、派生クラスのコンストラクターから基底クラスのコンストラクターを呼び出せます。
継承とコンストラクターの関係を理解しておくと、オブジェクト指向のコードが読みやすくなります。
9-1. 派生クラスのコンストラクターから基底クラスを初期化する
まず、基底クラスと派生クラスの例を見てみましょう。
C#public class Animal
{
public string Name { get; }
public Animal(string name)
{
Name = name;
}
}
public class Dog : Animal
{
public string Breed { get; }
public Dog(string name, string breed) : base(name)
{
Breed = breed;
}
}
DogクラスはAnimalクラスを継承しています。
C#public class Dog : Animal
Animalにはnameを受け取るコンストラクターがあります。
C#public Animal(string name)
{
Name = name;
}
そのため、Dogのコンストラクターではbase(name)を使って、親クラスであるAnimalのコンストラクターを呼び出しています。
9-2. base()を使って親クラスのコンストラクターを呼び出す方法
base()は、基底クラスのコンストラクターを呼び出すために使います。
C#public class Employee
{
public string Name { get; }
public Employee(string name)
{
Name = name;
}
}
public class Manager : Employee
{
public string Department { get; }
public Manager(string name, string department) : base(name)
{
Department = department;
}
}
使い方は次のとおりです。
C#Manager manager = new Manager("Sato", "Sales");
Console.WriteLine(manager.Name); // Sato
Console.WriteLine(manager.Department); // Sales
Managerのコンストラクターでbase(name)を呼び出すことで、親クラスのNameを初期化できます。
9-3. 親クラスと子クラスのコンストラクター実行順序
継承がある場合、コンストラクターは親クラスから先に実行され、その後に子クラスのコンストラクターが実行されます。
C#public class Parent
{
public Parent()
{
Console.WriteLine("Parentコンストラクター");
}
}
public class Child : Parent
{
public Child()
{
Console.WriteLine("Childコンストラクター");
}
}
次のコードを実行します。
C#Child child = new Child();
実行結果は次のようになります。
Parentコンストラクター
Childコンストラクター
これは、子クラスのオブジェクトを作るためには、まず親クラス部分を初期化する必要があるためです。
9-4. 継承時によくあるコンパイルエラーと対処法
継承時によくあるエラーは、基底クラスに引数なしコンストラクターがないのに、派生クラスでbase()を指定していないケースです。
C#public class Animal
{
public string Name { get; }
public Animal(string name)
{
Name = name;
}
}
public class Dog : Animal
{
public Dog()
{
}
}
このコードはエラーになります。
Animalには引数なしコンストラクターがなく、Animal(string name)だけがあるためです。
対処法は、派生クラスのコンストラクターからbase(name)を呼ぶことです。
C#public class Dog : Animal
{
public Dog(string name) : base(name)
{
}
}
または、基底クラスに引数なしコンストラクターを追加します。
C#public class Animal
{
public string Name { get; }
public Animal()
{
Name = "Unknown";
}
public Animal(string name)
{
Name = name;
}
}
どちらを選ぶかは、クラスの設計によります。必ず名前が必要なら、base(name)を使って明示的に初期化するほうが安全です。
9-5. base()とthis()の違い
base()とthis()はどちらもコンストラクター呼び出しに使いますが、呼び出す相手が違います。
base()は、基底クラスのコンストラクターを呼び出します。
C#public class Dog : Animal
{
public Dog(string name) : base(name)
{
}
}
一方、this()は、同じクラス内の別のコンストラクターを呼び出します。
C#public class User
{
public string Name { get; }
public int Age { get; }
public User() : this("Guest", 0)
{
}
public User(string name, int age)
{
Name = name;
Age = age;
}
}
違いをまとめると、次のようになります。
| キーワード | 呼び出すもの | 主な用途 |
|---|---|---|
| base() | 親クラスのコンストラクター | 継承時に親クラスを初期化する |
| this() | 同じクラスの別コンストラクター | コンストラクターの重複コードを減らす |
base()とthis()は、どちらもコンストラクター本体の前に書きます。
C#public Dog(string name) : base(name)
{
}
C#public User() : this("Guest", 0)
{
}
10. コンストラクターと初期化子の違い
C#では、コンストラクター以外にもオブジェクトを初期化する方法があります。その代表がオブジェクト初期化子です。
コンストラクターとオブジェクト初期化子は似ていますが、使いどころが異なります。
10-1. オブジェクト初期化子とは?
オブジェクト初期化子とは、インスタンス作成と同時にプロパティへ値を設定する書き方です。
C#public class User
{
public string Name { get; set; }
public int Age { get; set; }
}
このクラスは、次のように初期化できます。
C#User user = new User
{
Name = "Taro",
Age = 25
};
new User()の後に波かっこを書き、その中でプロパティに値を設定します。
これは次のコードと似た意味になります。
C#User user = new User();
user.Name = "Taro";
user.Age = 25;
ただし、オブジェクト初期化子を使うと、生成と設定をまとめて書けるため読みやすくなります。
10-2. コンストラクターで初期化する場合のメリット
コンストラクターで初期化するメリットは、必要な値を必ず渡させることができる点です。
C#public class User
{
public string Name { get; }
public int Age { get; }
public User(string name, int age)
{
Name = name;
Age = age;
}
}
このクラスでは、NameとAgeを指定しないとインスタンスを作れません。
C#User user = new User("Taro", 25);
次のようなコードは書けません。
C#User user = new User(); // エラー
つまり、コンストラクターを使うと「必須項目」を強制しやすくなります。
また、入力値チェックもコンストラクター内に書けます。
C#public User(string name, int age)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("名前は必須です", nameof(name));
}
if (age < 0)
{
throw new ArgumentOutOfRangeException(nameof(age));
}
Name = name;
Age = age;
}
不正な値のオブジェクトを作らせたくない場合は、コンストラクターが有効です。
10-3. オブジェクト初期化子で初期化する場合のメリット
オブジェクト初期化子のメリットは、任意の値を柔軟に設定しやすい点です。
C#public class SearchCondition
{
public string Keyword { get; set; }
public int Page { get; set; }
public int PageSize { get; set; }
}
このような検索条件クラスでは、すべての値が必須とは限りません。
C#SearchCondition condition = new SearchCondition
{
Keyword = "C#",
Page = 1,
PageSize = 20
};
オブジェクト初期化子は、プロパティが多いクラスでも読みやすく書けます。
C#User user = new User
{
Name = "Taro",
Age = 25,
Email = "taro@example.com",
Address = "Tokyo"
};
ただし、プロパティにsetが必要なため、外部から値を変更できる設計になりやすい点には注意が必要です。
10-4. コンストラクターと初期化子を組み合わせるサンプルコード
コンストラクターとオブジェクト初期化子は、組み合わせて使うこともできます。
C#public class User
{
public string Name { get; }
public int Age { get; }
public string Email { get; set; }
public User(string name, int age)
{
Name = name;
Age = age;
}
}
使い方は次のとおりです。
C#User user = new User("Taro", 25)
{
Email = "taro@example.com"
};
この例では、NameとAgeはコンストラクターで必須として受け取り、Emailは任意項目としてオブジェクト初期化子で設定しています。
このように、必須項目はコンストラクター、任意項目はオブジェクト初期化子にすると、分かりやすい設計になります。
10-5. 初心者はどちらを使えばよいか
初心者は、まず次の基準で使い分けるとよいです。
必ず必要な値はコンストラクターで受け取ります。
C#public User(string name, int age)
{
Name = name;
Age = age;
}
あとから設定してもよい任意の値は、オブジェクト初期化子で設定します。
C#User user = new User("Taro", 25)
{
Email = "taro@example.com"
};
迷った場合は、「その値がない状態のオブジェクトが存在してもよいか」を考えると判断しやすくなります。
存在してはいけないならコンストラクター、存在してもよいなら初期化子、という考え方です。
11. コンストラクターでよくあるエラーと対処法
C#のコンストラクターは基本構文がシンプルですが、初心者がつまずきやすいエラーがいくつかあります。
ここでは、よくあるミスと対処法を解説します。
11-1. クラス名とコンストラクター名が一致していない
コンストラクター名は、必ずクラス名と同じにする必要があります。
次のコードは誤りです。
C#public class User
{
public Person()
{
}
}
クラス名はUserなのに、コンストラクター名がPersonになっています。
正しくは次のように書きます。
C#public class User
{
public User()
{
}
}
クラス名を変更したときに、コンストラクター名を変更し忘れることがあるので注意しましょう。
11-2. 戻り値の型を書いてしまう
コンストラクターには戻り値を書きません。voidも不要です。
誤った例です。
C#public class User
{
public void User()
{
Console.WriteLine("初期化");
}
}
これはコンストラクターではなく、Userという名前の通常メソッドです。
正しくは次のように書きます。
C#public class User
{
public User()
{
Console.WriteLine("初期化");
}
}
コンストラクターは、戻り値を返すためではなく、インスタンスを初期化するための特別な処理です。
11-3. 引数なしコンストラクターがないためインスタンス化できない
引数ありコンストラクターを定義すると、引数なしコンストラクターは自動生成されません。
C#public class User
{
public string Name { get; }
public User(string name)
{
Name = name;
}
}
この場合、次のコードはエラーになります。
C#User user = new User(); // エラー
対処法は2つあります。
1つ目は、引数を渡してインスタンス化する方法です。
C#User user = new User("Taro");
2つ目は、引数なしコンストラクターを明示的に追加する方法です。
C#public class User
{
public string Name { get; }
public User()
{
Name = "Guest";
}
public User(string name)
{
Name = name;
}
}
11-4. thisやbaseの呼び出し位置を間違える
this()やbase()は、コンストラクター本体の中に書くのではなく、コンストラクター宣言の後ろに書きます。
誤った例です。
C#public class User
{
public User()
{
this("Guest"); // エラー
}
public User(string name)
{
}
}
正しくは次のように書きます。
C#public class User
{
public User() : this("Guest")
{
}
public User(string name)
{
}
}
base()も同じです。
C#public class Dog : Animal
{
public Dog(string name) : base(name)
{
}
}
this()とbase()は、コンストラクターの初期化処理が始まる前に呼び出す必要があるため、この位置に書きます。
11-5. nullや不正な値を初期化してしまう
コンストラクターで値を受け取るときは、nullや不正な値に注意しましょう。
C#public class User
{
public string Name { get; }
public User(string name)
{
Name = name;
}
}
このコードでは、次のようにnullを渡せてしまいます。
C#User user = new User(null);
これを防ぐには、コンストラクター内でチェックします。
C#public class User
{
public string Name { get; }
public User(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("名前は必須です", nameof(name));
}
Name = name;
}
}
数値の場合も同じです。
C#public class Product
{
public int Price { get; }
public Product(int price)
{
if (price < 0)
{
throw new ArgumentOutOfRangeException(nameof(price), "価格は0以上で指定してください");
}
Price = price;
}
}
不正な値を早い段階で防ぐことで、後続処理のバグを減らせます。
12. C#コンストラクターの実践サンプル
ここからは、実際に使える形に近いコンストラクターのサンプルを紹介します。
クラスを設計するときは、「どの値が必須か」「どの値をあとから変更できるか」「不正な値をどう防ぐか」を考えることが大切です。
12-1. Userクラスを作成して名前と年齢を初期化する
まずは、ユーザー情報を表すUserクラスです。
C#public class User
{
public string Name { get; }
public int Age { get; }
public User(string name, int age)
{
Name = name;
Age = age;
}
public void ShowProfile()
{
Console.WriteLine($"名前: {Name}, 年齢: {Age}");
}
}
使い方は次のとおりです。
C#User user = new User("Taro", 25);
user.ShowProfile();
実行結果です。
名前: Taro, 年齢: 25
NameとAgeを読み取り専用プロパティにしているため、作成後に外部から変更されません。
C#user.Name = "Hanako"; // エラー
このように、コンストラクターで初期化し、あとから変更させたくない値はgetのみのプロパティにすると安全です。
12-2. Productクラスで価格や在庫数を初期化する
次に、商品情報を表すProductクラスです。
C#public class Product
{
public string Name { get; }
public int Price { get; }
public int Stock { get; private set; }
public Product(string name, int price, int stock)
{
Name = name;
Price = price;
Stock = stock;
}
public void AddStock(int quantity)
{
Stock += quantity;
}
public void ReduceStock(int quantity)
{
Stock -= quantity;
}
}
使い方です。
C#Product product = new Product("Keyboard", 5000, 10);
Console.WriteLine(product.Name); // Keyboard
Console.WriteLine(product.Price); // 5000
Console.WriteLine(product.Stock); // 10
Stockは在庫数なので、あとから変更される可能性があります。ただし、外部から自由に変更されると危険です。
そのため、private setにして、在庫を変更する専用メソッドを用意しています。
C#product.AddStock(5);
product.ReduceStock(2);
このように、コンストラクターで初期値を設定し、変更ルールはメソッドにまとめると、クラスの責任が分かりやすくなります。
12-3. 引数チェックを含めた安全なコンストラクター例
実務では、コンストラクターに値チェックを入れることが多くあります。
C#public class Product
{
public string Name { get; }
public int Price { get; }
public int Stock { get; private set; }
public Product(string name, int price, int stock)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("商品名は必須です", nameof(name));
}
if (price < 0)
{
throw new ArgumentOutOfRangeException(nameof(price), "価格は0以上で指定してください");
}
if (stock < 0)
{
throw new ArgumentOutOfRangeException(nameof(stock), "在庫数は0以上で指定してください");
}
Name = name;
Price = price;
Stock = stock;
}
}
このクラスでは、次のような不正なインスタンスを作れません。
C#Product product = new Product("", -100, -1);
コンストラクターで不正な値を弾くことで、常に正しい状態のオブジェクトを作れます。
これは、オブジェクト指向で重要な「不変条件」を守る考え方にもつながります。
12-4. 複数のコンストラクターを使い分ける例
複数の作り方を用意したい場合は、コンストラクターのオーバーロードを使います。
C#public class Product
{
public string Name { get; }
public int Price { get; }
public int Stock { get; }
public Product(string name) : this(name, 0, 0)
{
}
public Product(string name, int price) : this(name, price, 0)
{
}
public Product(string name, int price, int stock)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("商品名は必須です", nameof(name));
}
if (price < 0)
{
throw new ArgumentOutOfRangeException(nameof(price));
}
if (stock < 0)
{
throw new ArgumentOutOfRangeException(nameof(stock));
}
Name = name;
Price = price;
Stock = stock;
}
}
使い方です。
C#Product p1 = new Product("Pen");
Product p2 = new Product("Notebook", 500);
Product p3 = new Product("Bag", 3000, 10);
この例では、最終的な初期化処理を引数3つのコンストラクターに集約しています。
これにより、値チェックや代入処理を重複して書かずに済みます。
12-5. 実務で読みやすいコンストラクターを書くコツ
実務で読みやすいコンストラクターを書くには、次の点を意識しましょう。
1つ目は、必須の値だけをコンストラクター引数にすることです。
C#public User(string name, int age)
{
}
必要以上に引数を増やすと、呼び出し側が分かりにくくなります。
C#public User(string name, int age, string email, string address, string phone, string note)
{
}
このように引数が多くなりすぎる場合は、設計を見直すか、設定用のクラスを使うことを検討します。
2つ目は、値チェックをコンストラクターの前半にまとめることです。
C#public User(string name, int age)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("名前は必須です", nameof(name));
}
if (age < 0)
{
throw new ArgumentOutOfRangeException(nameof(age));
}
Name = name;
Age = age;
}
3つ目は、重複コードを避けることです。
複数のコンストラクターがある場合は、this()を使って共通処理をまとめると読みやすくなります。
C#public User() : this("Guest", 0)
{
}
public User(string name, int age)
{
Name = name;
Age = age;
}
コンストラクターはクラスの使いやすさに直結するため、分かりやすく、安全に書くことが大切です。
13. C#コンストラクターに関するよくある質問
最後に、C#のコンストラクターについて初心者が疑問に感じやすいポイントを解説します。
13-1. コンストラクターは必ず書く必要がある?
コンストラクターは必ず書く必要はありません。
クラスにコンストラクターを1つも定義しない場合、C#は引数なしのデフォルトコンストラクターを自動的に用意します。
C#public class User
{
public string Name { get; set; }
}
このクラスは、次のようにインスタンス化できます。
C#User user = new User();
ただし、初期化したい値がある場合や、必須の値を強制したい場合は、コンストラクターを書くべきです。
C#public class User
{
public string Name { get; }
public User(string name)
{
Name = name;
}
}
このようにすると、Nameを必ず指定してUserを作成できます。
13-2. コンストラクターにreturnは書ける?
コンストラクターに戻り値は書けませんが、return;だけで処理を途中終了することはできます。
C#public class Sample
{
public Sample(bool skip)
{
if (skip)
{
return;
}
Console.WriteLine("初期化処理");
}
}
ただし、コンストラクター内でreturn 値;のように値を返すことはできません。
C#public Sample()
{
return 1; // エラー
}
コンストラクターは値を返すためのものではなく、インスタンスを初期化するためのものです。
また、コンストラクター内で早期returnを多用すると、初期化されないプロパティが発生しやすくなります。基本的には、入力値チェックで例外を投げるなど、分かりやすい書き方を選ぶのがおすすめです。
13-3. コンストラクターは非同期処理にできる?
C#のコンストラクターにasyncは付けられません。
次のような書き方はできません。
C#public async User()
{
}
コンストラクターはインスタンス生成のための特別な処理であり、Taskを返す非同期メソッドにはできないためです。
非同期処理が必要な場合は、staticのファクトリーメソッドを使う方法があります。
C#public class User
{
public string Name { get; private set; }
private User()
{
}
public static async Task<User> CreateAsync()
{
User user = new User();
await Task.Delay(1000);
user.Name = "Taro";
return user;
}
}
使い方は次のとおりです。
C#User user = await User.CreateAsync();
このように、非同期の初期化が必要な場合は、コンストラクターではなくCreateAsyncのようなメソッドを使うのが一般的です。
13-4. コンストラクターとinitプロパティの違いは?
initプロパティは、オブジェクト初期化時だけ値を設定できるプロパティです。
C#public class User
{
public string Name { get; init; }
public int Age { get; init; }
}
使い方は次のとおりです。
C#User user = new User
{
Name = "Taro",
Age = 25
};
初期化後に変更しようとするとエラーになります。
C#user.Name = "Hanako"; // エラー
コンストラクターとの違いは、値を渡す方法です。
コンストラクターでは、引数として値を渡します。
C#public class User
{
public string Name { get; }
public int Age { get; }
public User(string name, int age)
{
Name = name;
Age = age;
}
}
C#User user = new User("Taro", 25);
initプロパティでは、オブジェクト初期化子で値を設定します。
C#User user = new User
{
Name = "Taro",
Age = 25
};
必須項目を明確にしたい場合はコンストラクター、初期化時の記述を柔軟にしたい場合はinitプロパティが便利です。
13-5. コンストラクター内で例外を投げてもよい?
コンストラクター内で例外を投げても問題ありません。
むしろ、不正な状態のオブジェクトを作らせないために、コンストラクターで例外を投げることはよくあります。
C#public class User
{
public string Name { get; }
public User(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentException("名前は必須です", nameof(name));
}
Name = name;
}
}
このようにすると、名前が空のUserを作成できません。
C#User user = new User(""); // 例外
ただし、コンストラクターで重い処理や失敗しやすい処理を大量に行うと、インスタンス生成が扱いにくくなることがあります。
たとえば、外部API通信、ファイル読み込み、データベース接続などは、コンストラクターではなく別メソッドやファクトリーメソッドに分けることも検討しましょう。
コンストラクターでは、基本的に「値の検証」と「必要な初期化」に集中させると、読みやすく保守しやすいコードになります。
まとめ
C#のコンストラクターは、インスタンス生成時に初期化処理を行うための特別な仕組みです。
コンストラクターはクラス名と同じ名前で定義し、戻り値を書きません。new演算子でインスタンスを作成するときに自動的に呼び出されます。
基本的なコンストラクターは次のように書きます。
C#public class User
{
public string Name { get; }
public User(string name)
{
Name = name;
}
}
デフォルトコンストラクターは引数なしのコンストラクターです。クラスにコンストラクターを1つも定義しない場合は自動生成されますが、引数ありコンストラクターを定義すると自動生成されなくなる点に注意が必要です。
引数ありコンストラクターを使うと、オブジェクト生成時に必要な値を渡して初期化できます。また、thisを使うことで、引数とフィールドを明確に区別できます。
複数のコンストラクターを定義するオーバーロードや、this()を使ったコンストラクターチェーンを使えば、さまざまな生成方法を用意しながら重複コードを減らせます。
さらに、C#には静的コンストラクター、privateコンストラクター、継承時のbase()、オブジェクト初期化子、initプロパティなど、コンストラクターに関連する重要な仕組みがあります。
初心者はまず、次のポイントを押さえましょう。
コンストラクターはインスタンス生成時に呼ばれる
クラス名と同じ名前で定義する
戻り値を書かない
必須の値はコンストラクターで受け取る
不正な値はコンストラクター内でチェックする
重複する初期化処理は
this()でまとめる継承時は
base()で親クラスを初期化する
C#のコンストラクターを正しく使えるようになると、クラス設計が分かりやすくなり、安全で保守しやすいコードを書けるようになります。

