コンストラクタにおいて、基底クラスのコンストラクタを指定したい場合は base
を、同じクラス内の別のコンストラクタを指定したい場合は this
を指定しますが、両方指定したい場合はどうすればいいのか?
結論から先に書くと、両方を指定することはできません。
恐らくですが、base
と this
を両方指定したいと思うということは、コンストラクタ内で行う処理を全て共通化したいということなのだと思います。
私の場合はそうでした。
そして、私の場合はデフォルト引数を使うことで解決しました。
また、base
や this
を指定した場合(または指定しなかった場合)に、どのようにコンストラクタが呼ばれるかをきちんと把握すれば、もしかしたらどちらかを指定するだけで事足りるかもしれません。
というわけでいろいろ試してみました。
環境
- Visual Studio 2019
- .NET Core 3.1
サンプル1
基底クラス SampleBase
と、派生クラス Sample
に、それぞれ引数なしと引数ありのコンストラクタを作成しています。
パターン1
base
も this
も指定しない場合です。
public static void Main()
{
new Sample();
Console.WriteLine("----------");
new Sample("foo");
}
/// <summary>
/// 基底クラス
/// </summary>
abstract class SampleBase
{
public SampleBase()
{
Console.WriteLine("SampleBase コンストラクタ引数なし");
}
public SampleBase(string foo)
{
Console.WriteLine($"SampleBase コンストラクタ引数あり:{foo}");
}
}
/// <summary>
/// 派生クラス
/// </summary>
class Sample : SampleBase
{
public Sample()
{
Console.WriteLine("Sample コンストラクタ引数なし");
}
public Sample(string foo)
{
Console.WriteLine($"Sample コンストラクタ引数あり:{foo}");
}
}
出力結果
SampleBase コンストラクタ引数なし
Sample コンストラクタ引数なし
----------
SampleBase コンストラクタ引数なし
Sample コンストラクタ引数あり:foo
派生クラス Sample
は引数の有無によって呼ばれるコンストラクタが違いますが、基底クラス SampleBase
はいずれの場合も引数なしのコンストラクタが呼ばれています。
パターン2
パターン1をベースに、基底クラスの引数ありコンストラクタを使うために、派生クラス Sample
で base
を指定しています。
public static void Main()
{
new Sample();
Console.WriteLine("----------");
new Sample("foo");
}
/// <summary>
/// 基底クラス
/// </summary>
abstract class SampleBase
{
public SampleBase()
{
Console.WriteLine("SampleBase コンストラクタ引数なし");
}
public SampleBase(string foo)
{
Console.WriteLine($"SampleBase コンストラクタ引数あり:{foo}");
}
}
/// <summary>
/// 派生クラス
/// </summary>
class Sample : SampleBase
{
public Sample()
{
Console.WriteLine("Sample コンストラクタ引数なし");
}
public Sample(string foo) : base(foo)
{
Console.WriteLine($"Sample コンストラクタ引数あり:{foo}");
}
}
出力結果
SampleBase コンストラクタ引数なし
Sample コンストラクタ引数なし
----------
SampleBase コンストラクタ引数あり:foo
Sample コンストラクタ引数あり:foo
引数を指定しなかった場合は、Sample
SampleBase
ともに引数なしのコンストラクタがコールされます。
引数を指定した場合は、Sample
SampleBase
ともに引数ありのコンストラクタがコールされます。
まぁここらへんまでは当たり前ですね。
パターン3
パターン2をベースに、基底クラス SampleBase
の引数ありコンストラクタで this
を指定しています。
public static void Main()
{
new Sample();
Console.WriteLine("----------");
new Sample("foo");
}
/// <summary>
/// 基底クラス
/// </summary>
abstract class SampleBase
{
public SampleBase()
{
Console.WriteLine("SampleBase コンストラクタ引数なし");
}
public SampleBase(string foo) : this()
{
Console.WriteLine($"SampleBase コンストラクタ引数あり:{foo}");
}
}
/// <summary>
/// 派生クラス
/// </summary>
class Sample : SampleBase
{
public Sample()
{
Console.WriteLine("Sample コンストラクタ引数なし");
}
public Sample(string foo) : base(foo)
{
Console.WriteLine($"Sample コンストラクタ引数あり:{foo}");
}
}
出力結果
SampleBase コンストラクタ引数なし
Sample コンストラクタ引数なし
----------
SampleBase コンストラクタ引数なし
SampleBase コンストラクタ引数あり:foo
Sample コンストラクタ引数あり:foo
引数を指定した場合、基底クラスのコンストラクタは両方呼ばれます。
パターン4
パターン3をベースに、派生クラス Sample
の引数なしコンストラクタで基底クラス SampleBase
の引数ありコンストラクタを呼んでいます。
public static void Main()
{
new Sample();
Console.WriteLine("----------");
new Sample("foo");
}
/// <summary>
/// 基底クラス
/// </summary>
abstract class SampleBase
{
public SampleBase()
{
Console.WriteLine("SampleBase コンストラクタ引数なし");
}
public SampleBase(string foo) : this()
{
Console.WriteLine($"SampleBase コンストラクタ引数あり:{foo}");
}
}
/// <summary>
/// 派生クラス
/// </summary>
class Sample : SampleBase
{
public Sample() : base(null)
{
Console.WriteLine("Sample コンストラクタ引数なし");
}
public Sample(string foo) : this()
{
Console.WriteLine($"Sample コンストラクタ引数あり:{foo}");
}
}
出力結果
SampleBase コンストラクタ引数なし
SampleBase コンストラクタ引数あり:
Sample コンストラクタ引数なし
----------
SampleBase コンストラクタ引数なし
SampleBase コンストラクタ引数あり:
Sample コンストラクタ引数なし
Sample コンストラクタ引数あり:foo
引数を指定した場合は全てのコンストラクタが呼ばれています。
多分これが一番問題のパターン。
派生クラス Sample
の引数の値を基底クラス SampleBase
に渡しつつ、コンストラクタ内の処理は各クラスの引数なしコンストラクタに集約したい。
しかし、派生クラス Sample
の引数ありコンストラクタで base
と this
の両方を指定することはできません。
かと言って、このパターンのようにやると this
を指定したときに引数の情報が失われてしまいます(当たり前ですが)。
というわけで、代替案が次のサンプルです。
サンプル2(代替案)
派生クラス Sample
のコンストラクタを引数ありの1つのみにし、デフォルト引数を定義する方法です。
public static void Main()
{
new Sample();
Console.WriteLine("----------");
new Sample("foo");
}
/// <summary>
/// 基底クラス
/// </summary>
abstract class SampleBase
{
public SampleBase()
{
Console.WriteLine("SampleBase コンストラクタ引数なし");
}
public SampleBase(string foo) : this()
{
Console.WriteLine($"SampleBase コンストラクタ引数あり:{foo}");
}
}
/// <summary>
/// 派生クラス
/// </summary>
class Sample : SampleBase
{
public Sample(string foo = null) : base(foo)
{
Console.WriteLine($"Sample コンストラクタ引数あり:{foo}");
}
}
出力結果
SampleBase コンストラクタ引数なし
SampleBase コンストラクタ引数あり:
Sample コンストラクタ引数あり:
----------
SampleBase コンストラクタ引数なし
SampleBase コンストラクタ引数あり:foo
Sample コンストラクタ引数あり:foo
私の場合はこれで解決しました。