【C#】コンストラクタのオーバーロードと「this」による呼び出し:初期化ロジックの共通化

目次

オブジェクト初期化の多様性

クラスや構造体を設計する際、インスタンスの生成方法(初期化のパターン)を複数提供したい場合があります。例えば、すべての値を個別に指定して生成する場合もあれば、一つの値を基準に残りを自動設定して生成する場合もあります。

C#では、引数の構成が異なるコンストラクタを複数定義する「オーバーロード」と、あるコンストラクタから別のコンストラクタを呼び出す「コンストラクタ初期化子(: this(...)」を組み合わせることで、柔軟かつ保守性の高い初期化ロジックを実装できます。

この記事では、コンストラクタのオーバーロードの基本と、重複コードを防ぐためのコンストラクタ連鎖について解説します。


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

メソッドと同様に、コンストラクタも引数の数や型が異なれば、同じクラス内に複数定義することができます。

例えば、長方形を表すRectangle構造体を考えます。

  1. 幅と高さを個別に指定する(長方形)
  2. 1辺の長さだけを指定する(正方形)

この2つのパターンに対応するために、2つのコンストラクタを定義します。


thisキーワードによるコンストラクタの連鎖

複数のコンストラクタを定義する際、それぞれの内部でフィールドへの代入処理を個別に記述すると、コードが重複し、修正漏れの原因となります。

これを防ぐために、thisキーワードを使用して、あるコンストラクタから「メインとなるコンストラクタ」を呼び出す手法が推奨されます。これを「コンストラクタの連鎖(Constructor Chaining)」と呼びます。

構文: public ClassName(args) : this(mainArgs)


コード例:長方形と正方形の初期化

以下の例では、幅と高さを指定する「メインのコンストラクタ」を定義し、正方形用のコンストラクタからそれを呼び出しています。

using System;

public class Program
{
    public static void Main()
    {
        // 1. 幅と高さを指定して初期化 (メインコンストラクタ)
        var rect = new Rectangle(10, 20);
        Console.WriteLine($"長方形: 幅={rect.Width}, 高さ={rect.Height}");

        // 2. 1辺の長さを指定して初期化 (連鎖コンストラクタ)
        // 内部で Rectangle(5, 5) が呼び出される
        var square = new Rectangle(5);
        Console.WriteLine($"正方形: 幅={square.Width}, 高さ={square.Height}");
    }
}

/// <summary>
/// 長方形を表す読み取り専用構造体
/// </summary>
public readonly struct Rectangle
{
    public int Width { get; }
    public int Height { get; }

    // --- メインのコンストラクタ ---
    // すべてのプロパティを初期化する責任を持つ
    public Rectangle(int width, int height)
    {
        Width = width;
        Height = height;
    }

    // --- オーバーロードされたコンストラクタ ---
    // 正方形の場合、幅と高さは同じ値になる。
    // : this(size, size) と記述することで、
    // 上記のメインコンストラクタ Rectangle(int, int) に処理を委譲する。
    public Rectangle(int size) : this(size, size)
    {
        // ここには追加の初期化ロジックがあれば記述する。
        // 基本的な代入はメインコンストラクタで行われているため、空でもよい。
    }
}

出力結果:

長方形: 幅=10, 高さ=20
正方形: 幅=5, 高さ=5

オプション引数との比較

C# 4.0以降では「オプション引数(デフォルト引数)」が利用可能になったため、単純な初期化であればオーバーロードを使わずに1つのコンストラクタで完結させることも可能です。

// オプション引数を使用した例
public Rectangle(int width, int height = 0)
{
    // height が省略された場合のロジックなどが必要になる場合がある
    Width = width;
    Height = (height == 0) ? width : height;
}

しかし、引数の意味が大きく変わる場合(例: intでIDを指定する場合と、stringで名前を指定する場合など)や、引数の組み合わせが複雑な場合は、thisを使用した明示的なオーバーロードの方が、コードの意図が明確になり、可読性が高まります。


まとめ

コンストラクタのオーバーロードとthisキーワードによる連鎖は、クラスの初期化ロジックを一箇所(メインコンストラクタ)に集約するための重要なテクニックです。

  • オーバーロード: 異なる引数パターンでインスタンス生成を可能にします。
  • thisによる連鎖: 共通の初期化処理を再利用し、コードの重複(DRY原則違反)を防ぎます。

これにより、利用者は柔軟にオブジェクトを生成でき、開発者は保守性の高いコードを維持できます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次