0Pricing
C# Academy · レッスン

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

コンストラクター間で初期化処理を再利用します。

「thisによるコンストラクターの連鎖」はCoddyKit上の無料C# Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはC# Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 C# Academyコースには全4レッスンが含まれています。

コンストラクターをチェーンする理由

複数のコンストラクターでセットアップ処理を共有する場合は、一方からもう一方を呼び出すようにチェーンできます。これによりコードの重複を避け、初期化処理を1か所にまとめられます。

using System;

class Box
{
    public int Width;
    public int Height;

    public Box(int width, int height)
    {
        Width = width;
        Height = height;
    }

    public Box() : this(1, 1) { }
}

class Program
{
    static void Main()
    {
        var def = new Box();
        Console.WriteLine(def.Width + "x" + def.Height);
    }
}

<code>: this(...)</code> 構文

チェーンには、コンストラクターのシグネチャの後に : this(args) を記述します。対象のコンストラクターが先に実行され、その後で現在のコンストラクターの本体が実行されます。

using System;

class Person
{
    public string Name;
    public int Age;

    public Person(string name, int age)
    {
        Name = name;
        Age = age;
        Console.WriteLine("Full constructor ran");
    }

    public Person(string name) : this(name, 0)
    {
        Console.WriteLine("Name-only constructor ran");
    }
}

class Program
{
    static void Main()
    {
        var p = new Person("Sam");
        Console.WriteLine(p.Name + ", " + p.Age);
    }
}

実行順序

呼び出し元のコンストラクターの本体より先に、チェーンされたコンストラクターが実行されます。これにより、基礎となるセットアップが必ず先に行われます。

using System;

class Step
{
    public Step(int n)
    {
        Console.WriteLine("Init with " + n);
    }

    public Step() : this(0)
    {
        Console.WriteLine("Then default body");
    }
}

class Program
{
    static void Main()
    {
        var s = new Step();
    }
}

信頼できる唯一の情報源

すべてのコンストラクターを、実際のロジックを持つ1つの「主要」コンストラクターに集約します。それ以外のコンストラクターは既定値を指定して、そこへチェーンするだけにします。

using System;

class Connection
{
    public string Host;
    public int Port;

    public Connection(string host, int port)
    {
        Host = host;
        Port = port;
    }

    public Connection(string host) : this(host, 8080) { }
    public Connection() : this("localhost", 8080) { }
}

class Program
{
    static void Main()
    {
        var c = new Connection("example.com");
        Console.WriteLine(c.Host + ":" + c.Port);
    }
}

既定値を使ったチェーン

チェーンを使うと、段階的な既定値をわかりやすく表現できます。それぞれの短いコンストラクターが追加の既定値を1つ設定し、処理を委譲します。

using System;

class Coffee
{
    public string Size;
    public bool Milk;
    public int Sugar;

    public Coffee(string size, bool milk, int sugar)
    {
        Size = size;
        Milk = milk;
        Sugar = sugar;
    }

    public Coffee(string size, bool milk) : this(size, milk, 0) { }
    public Coffee(string size) : this(size, false) { }
}

class Program
{
    static void Main()
    {
        var c = new Coffee("large");
        Console.WriteLine(c.Size + ", milk=" + c.Milk + ", sugar=" + c.Sugar);
    }
}

対象側での検証

すべてのコンストラクターが主要コンストラクターを経由するため、そこに記述した検証によって、すべての生成経路を保護できます。

using System;

class Rect
{
    public int W;
    public int H;

    public Rect(int w, int h)
    {
        if (w <= 0 || h <= 0)
            throw new ArgumentException("Dimensions must be positive");
        W = w;
        H = h;
    }

    public Rect(int side) : this(side, side) { }
}

class Program
{
    static void Main()
    {
        var square = new Rect(5);
        Console.WriteLine(square.W + "x" + square.H);
    }
}

チェーンと既定のパラメーター

既定のパラメーターを使えば多くのオーバーロードを置き換えられますが、それぞれのコンストラクターの本体で少しずつ異なる処理が必要な場合は、チェーンのほうが明確です。

using System;

class Logger
{
    public string Prefix;
    public bool Verbose;

    public Logger(string prefix, bool verbose)
    {
        Prefix = prefix;
        Verbose = verbose;
    }

    public Logger(string prefix) : this(prefix, false)
    {
        Console.WriteLine("Created quiet logger");
    }
}

class Program
{
    static void Main()
    {
        var log = new Logger("APP");
        Console.WriteLine(log.Prefix + " verbose=" + log.Verbose);
    }
}

空の本体は一般的です

チェーンするコンストラクターでは、すべての処理が対象側で行われるため、{ } の本体が空になることがよくあります。

using System;

class Vector
{
    public double X, Y, Z;

    public Vector(double x, double y, double z)
    {
        X = x; Y = y; Z = z;
    }

    public Vector(double v) : this(v, v, v) { }
}

class Program
{
    static void Main()
    {
        var v = new Vector(2);
        Console.WriteLine(v.X + "," + v.Y + "," + v.Z);
    }
}

循環の回避

コンストラクターをループ状にチェーンしてはいけません(A が B を呼び出し、B が A を呼び出す状態です)。コンパイラーは循環する : this(...) のチェーンを拒否します。

using System;

class Safe
{
    public int A, B;

    public Safe(int a, int b)
    {
        A = a;
        B = b;
    }

    public Safe(int a) : this(a, 0) { }   // chains one direction only
}

class Program
{
    static void Main()
    {
        var s = new Safe(7);
        Console.WriteLine(s.A + ", " + s.B);
    }
}

this.field との組み合わせ

チェーンを使う場合でも、本体で this. を使って、対象のコンストラクターから戻った後に追加のセットアップを行えます。

using System;

class Account
{
    public string Owner;
    public decimal Balance;
    public bool Active;

    public Account(string owner, decimal balance)
    {
        this.Owner = owner;
        this.Balance = balance;
    }

    public Account(string owner) : this(owner, 0m)
    {
        this.Active = true;
    }
}

class Program
{
    static void Main()
    {
        var a = new Account("Rin");
        Console.WriteLine(a.Owner + " active=" + a.Active);
    }
}

まとめてみましょう

チェーンを使うと、クラスを DRY に保てます。1つのコンストラクターで検証と代入を行い、それ以外のコンストラクターは便利な短縮記法を提供します。

using System;

class Pizza
{
    public string Size;
    public int Toppings;

    public Pizza(string size, int toppings)
    {
        if (toppings < 0) throw new ArgumentException("toppings");
        Size = size;
        Toppings = toppings;
    }

    public Pizza(string size) : this(size, 0) { }
    public Pizza() : this("medium") { }
}

class Program
{
    static void Main()
    {
        var p = new Pizza();
        Console.WriteLine(p.Size + " with " + p.Toppings + " toppings");
    }
}

理解度チェック

コンストラクターのチェーンについての理解度を確認しましょう。

まとめ

コンストラクターのチェーンでは、: this(args) を使って同じクラス内の別のコンストラクターを呼び出します。対象のコンストラクターが先に実行され、その後で現在の本体が実行されます。すべてのコンストラクターを1つの主要コンストラクターに通すことで、検証を一元化し、セットアップ処理の重複を避けられます。循環するチェーンは使用できません。

using System;

class Demo
{
    public int X, Y;
    public Demo(int x, int y) { X = x; Y = y; }
    public Demo(int both) : this(both, both) { }
}

class Program
{
    static void Main()
    {
        var d = new Demo(4);
        Console.WriteLine(d.X + "," + d.Y);
    }
}

よくある質問

「thisによるコンストラクターの連鎖」レッスンは無料ですか?

はい。「thisによるコンストラクターの連鎖」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、C# Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 C# Academyコースには全4レッスンが含まれています。

「thisによるコンストラクターの連鎖」で何を学びますか?

コンストラクター間で初期化処理を再利用します。 ブラウザで直接実行するハンズオンコードでC# Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

C# Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのC# Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「thisによるコンストラクターの連鎖」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このC# Academyレッスンでコードを書いて実行できますか?

はい。すべてのC# Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. コンストラクターの定義
  2. thisによるコンストラクターの連鎖
  3. オブジェクトとコレクションの初期化子
  4. 静的コンストラクター
← C# Academyに戻る