C#のbaseとは?継承・親クラス呼び出し・コンストラクタの使い方を初心者向けに解説
はじめに
C#でクラスの継承を学び始めると、baseというキーワードがよく登場します。
baseは、子クラスから親クラスのメソッド、プロパティ、コンストラクタなどを呼び出すために使うキーワードです。特に、継承、オーバーライド、コンストラクタの初期化を理解するうえで重要な役割を持っています。
たとえば、親クラスに共通処理を用意しておき、子クラスではその処理を利用しながら独自の処理を追加したい場合にbaseを使います。
C#class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
class Dog : Animal
{
public override void Speak()
{
base.Speak();
Console.WriteLine("犬がワンと鳴きます");
}
}
この例では、Dogクラスの中からAnimalクラスのSpeakメソッドをbase.Speak()で呼び出しています。
この記事では、C#のbaseについて、継承の基本から親クラスのメソッド呼び出し、コンストラクタでの使い方、thisとの違い、よくあるエラーまで初心者向けにわかりやすく解説します。
1. C#のbaseとは?初心者向けに意味をわかりやすく解説
C#のbaseは、継承関係にあるクラスで「親クラス側のメンバー」を明示的に呼び出すためのキーワードです。
クラスを継承すると、子クラスは親クラスのメソッドやプロパティを利用できます。しかし、子クラスで同じ名前のメソッドを定義した場合や、親クラスのコンストラクタに値を渡したい場合には、親クラス側を明確に指定する必要があります。
そのときに使うのがbaseです。
1-1. baseは「親クラス(基底クラス)」のメンバーを呼び出すキーワード
baseは、現在のクラスから見た親クラスを表します。
C#では、継承元のクラスを「基底クラス」または「親クラス」と呼び、継承先のクラスを「派生クラス」または「子クラス」と呼びます。
C#class Parent
{
public void ShowMessage()
{
Console.WriteLine("親クラスのメソッドです");
}
}
class Child : Parent
{
public void Execute()
{
base.ShowMessage();
}
}
このコードでは、Childクラスの中でbase.ShowMessage()を呼び出しています。
base.ShowMessage()は、「親クラスであるParentクラスのShowMessageメソッドを呼び出す」という意味です。
1-2. baseを使う主な場面は「親メソッドの呼び出し」と「親コンストラクタの呼び出し」
baseを使う場面は主に2つあります。
1つ目は、親クラスのメソッドやプロパティを呼び出す場合です。
C#base.MethodName();
base.PropertyName;
2つ目は、子クラスのコンストラクタから親クラスのコンストラクタを呼び出す場合です。
C#public Child(string name) : base(name)
{
}
特に、親クラスに引数付きコンストラクタしかない場合は、子クラス側でbase(...)を使って親クラスのコンストラクタを明示的に呼び出す必要があります。
1-3. C#における親クラス・子クラス・基底クラス・派生クラスの関係
C#の継承では、あるクラスを元にして新しいクラスを作ることができます。
C#class Animal
{
}
class Dog : Animal
{
}
この場合、Animalが親クラス、Dogが子クラスです。
C#では次のような呼び方もします。
| 呼び方 | 意味 |
|---|---|
| 親クラス | 継承元のクラス |
| 子クラス | 継承先のクラス |
| 基底クラス | 親クラスと同じ意味 |
| 派生クラス | 子クラスと同じ意味 |
| スーパークラス | 親クラスを指すことがある |
| サブクラス | 子クラスを指すことがある |
C#の公式な文脈では「基底クラス」「派生クラス」という表現がよく使われます。
1-4. baseを理解するために必要な継承の基本
baseを理解するには、まず継承の基本を押さえる必要があります。
継承とは、既存のクラスの機能を引き継いで、新しいクラスを作る仕組みです。
C#class Animal
{
public string Name { get; set; }
public void Eat()
{
Console.WriteLine($"{Name}が食べています");
}
}
class Dog : Animal
{
public void Bark()
{
Console.WriteLine($"{Name}が吠えています");
}
}
DogクラスはAnimalクラスを継承しているため、AnimalクラスのNameプロパティやEatメソッドを使えます。
C#Dog dog = new Dog();
dog.Name = "ポチ";
dog.Eat();
dog.Bark();
このように、継承によって親クラスの機能を子クラスで再利用できます。
baseは、この継承関係の中で「親クラス側の機能を明示的に使いたい」ときに登場します。
2. C#の継承と親クラス呼び出しの基本
baseは継承とセットで使われます。そのため、C#でクラスを継承する基本的な書き方を理解しておきましょう。
2-1. C#でクラスを継承する書き方
C#でクラスを継承するには、クラス名の後ろにコロン:を書き、その後に継承したいクラス名を指定します。
C#class 子クラス : 親クラス
{
}
具体例は次のとおりです。
C#class Vehicle
{
public void Move()
{
Console.WriteLine("移動します");
}
}
class Car : Vehicle
{
public void Drive()
{
Console.WriteLine("車を運転します");
}
}
CarクラスはVehicleクラスを継承しています。そのため、CarクラスのインスタンスからMoveメソッドを呼び出せます。
C#Car car = new Car();
car.Move();
car.Drive();
このように、子クラスは親クラスの機能を引き継ぎます。
2-2. 継承すると親クラスのフィールド・プロパティ・メソッドを使える
親クラスに定義されたフィールド、プロパティ、メソッドは、アクセス修飾子の条件を満たしていれば子クラスから利用できます。
C#class Person
{
public string Name { get; set; }
protected int Age;
public void Introduce()
{
Console.WriteLine($"私は{Name}です");
}
}
class Student : Person
{
public void ShowAge()
{
Console.WriteLine($"年齢は{Age}歳です");
}
}
この例では、Nameはpublicなので外部からも子クラスからもアクセスできます。
Ageはprotectedなので、外部からはアクセスできませんが、子クラスであるStudentクラスからはアクセスできます。
2-3. public・protected・privateでbaseから呼び出せる範囲が変わる
baseを使えば何でも親クラスのメンバーを呼び出せるわけではありません。アクセス修飾子によって呼び出せる範囲が変わります。
C#class Parent
{
public void PublicMethod()
{
Console.WriteLine("publicメソッド");
}
protected void ProtectedMethod()
{
Console.WriteLine("protectedメソッド");
}
private void PrivateMethod()
{
Console.WriteLine("privateメソッド");
}
}
class Child : Parent
{
public void Test()
{
base.PublicMethod();
base.ProtectedMethod();
// base.PrivateMethod(); // エラー
}
}
publicメンバーはどこからでも呼び出せます。
protectedメンバーは、同じクラスまたは派生クラスから呼び出せます。
privateメンバーは、そのクラスの内部からしか呼び出せません。子クラスであっても、base.PrivateMethod()のように呼び出すことはできません。
2-4. C#はクラスの多重継承ができない点に注意
C#では、1つのクラスが複数のクラスを同時に継承することはできません。
C#class A
{
}
class B
{
}
// class C : A, B
// {
// }
// エラー:C#ではクラスの多重継承はできない
C#のクラス継承では、直接継承できる親クラスは1つだけです。
ただし、インターフェイスは複数実装できます。
C#interface IReadable
{
void Read();
}
interface IWritable
{
void Write();
}
class FileData : IReadable, IWritable
{
public void Read()
{
Console.WriteLine("読み込みます");
}
public void Write()
{
Console.WriteLine("書き込みます");
}
}
baseは親クラスを呼び出すためのキーワードなので、クラスの継承関係がある場合に使います。
3. baseを使って親クラスのメソッド・プロパティを呼び出す方法
baseの代表的な使い方は、親クラスのメソッドやプロパティを子クラスから呼び出すことです。
特に、子クラスで親クラスと同じ名前のメソッドやプロパティを持っている場合に、baseを使うことで親クラス側のメンバーを明示できます。
3-1. base.メソッド名()の基本構文
親クラスのメソッドを呼び出す基本構文は次のとおりです。
C#base.メソッド名();
例を見てみましょう。
C#class Parent
{
public void SayHello()
{
Console.WriteLine("こんにちは。親クラスです。");
}
}
class Child : Parent
{
public void SayHelloFromChild()
{
base.SayHello();
Console.WriteLine("こんにちは。子クラスです。");
}
}
実行例です。
C#Child child = new Child();
child.SayHelloFromChild();
出力結果は次のようになります。
C#こんにちは。親クラスです。
こんにちは。子クラスです。
base.SayHello()によって、親クラスのSayHelloメソッドが呼び出されています。
3-2. base.プロパティ名で親クラスのプロパティにアクセスする
baseはメソッドだけでなく、親クラスのプロパティにアクセスするときにも使えます。
C#class Person
{
public string Name { get; set; }
}
class Employee : Person
{
public void SetName(string name)
{
base.Name = name;
}
public void ShowName()
{
Console.WriteLine(base.Name);
}
}
この例では、base.Nameで親クラスPersonに定義されたNameプロパティにアクセスしています。
ただし、子クラス側に同名のプロパティがない場合は、単にNameと書いてもアクセスできます。
C#Name = name;
そのため、base.Nameは「親クラス側のプロパティであることを明示したい場合」に使うと考えるとわかりやすいです。
3-3. 子クラスで同名メンバーがあるときにbaseで親側を明示する
baseが特に役立つのは、親クラスと子クラスに同じ名前のメンバーがある場合です。
C#class Parent
{
public string Message { get; set; } = "親クラスのメッセージ";
}
class Child : Parent
{
public new string Message { get; set; } = "子クラスのメッセージ";
public void ShowMessages()
{
Console.WriteLine(this.Message);
Console.WriteLine(base.Message);
}
}
実行例です。
C#Child child = new Child();
child.ShowMessages();
出力結果は次のようになります。
C#子クラスのメッセージ
親クラスのメッセージ
this.Messageは子クラス自身のMessageを指します。
一方、base.Messageは親クラス側のMessageを指します。
このように、同名メンバーがある場合にbaseを使うと、どちらのメンバーを使っているのかが明確になります。
3-4. サンプルコードで見るbaseによる親クラス呼び出し
次の例では、親クラスReportに共通の出力処理を定義し、子クラスSalesReportでその処理を呼び出しています。
C#class Report
{
public void PrintHeader()
{
Console.WriteLine("=== レポート開始 ===");
}
public void PrintFooter()
{
Console.WriteLine("=== レポート終了 ===");
}
}
class SalesReport : Report
{
public void Print()
{
base.PrintHeader();
Console.WriteLine("売上データを出力します");
base.PrintFooter();
}
}
実行例です。
C#SalesReport report = new SalesReport();
report.Print();
出力結果は次のとおりです。
C#=== レポート開始 ===
売上データを出力します
=== レポート終了 ===
base.PrintHeader()とbase.PrintFooter()によって、親クラスの共通処理を再利用しています。
このように、共通処理を親クラスにまとめておくと、子クラスでは必要な部分だけを追加できます。
4. overrideとbaseの関係:親クラスの処理を残して拡張する
baseは、overrideと組み合わせて使われることがよくあります。
親クラスのメソッドを子クラスで上書きしつつ、親クラスの処理も実行したい場合にbase.メソッド名()を使います。
4-1. virtualとoverrideの基本
C#で親クラスのメソッドを子クラスで上書きするには、親クラス側にvirtual、子クラス側にoverrideを付けます。
C#class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("犬がワンと鳴きます");
}
}
virtualは「このメソッドは派生クラスでオーバーライドできます」という意味です。
overrideは「親クラスの仮想メソッドを子クラスで上書きします」という意味です。
4-2. base.メソッド名()でオーバーライド元の処理を呼び出す
子クラスでメソッドをoverrideすると、通常は子クラス側の処理が実行されます。
しかし、親クラスの処理も残したい場合があります。その場合は、子クラスのオーバーライドメソッド内でbase.メソッド名()を呼び出します。
C#class Animal
{
public virtual void Speak()
{
Console.WriteLine("動物が鳴きます");
}
}
class Dog : Animal
{
public override void Speak()
{
base.Speak();
Console.WriteLine("犬がワンと鳴きます");
}
}
実行例です。
C#Dog dog = new Dog();
dog.Speak();
出力結果は次のようになります。
C#動物が鳴きます
犬がワンと鳴きます
base.Speak()によって、オーバーライド元であるAnimalクラスのSpeakメソッドが実行されています。
4-3. 親の処理に子クラス独自の処理を追加する実装例
業務アプリ風の例で考えてみましょう。
親クラスで基本的な保存処理を行い、子クラスで追加のログ出力を行うケースです。
C#class Repository
{
public virtual void Save()
{
Console.WriteLine("データを保存しました");
}
}
class UserRepository : Repository
{
public override void Save()
{
base.Save();
Console.WriteLine("ユーザー情報の保存ログを出力しました");
}
}
実行例です。
C#UserRepository repository = new UserRepository();
repository.Save();
出力結果は次のとおりです。
C#データを保存しました
ユーザー情報の保存ログを出力しました
このように、base.Save()を使うことで親クラスの共通処理を残しながら、子クラス独自の処理を追加できます。
4-4. baseを使わない場合との動作の違い
baseを使わない場合、親クラスの処理は実行されません。
C#class Repository
{
public virtual void Save()
{
Console.WriteLine("データを保存しました");
}
}
class UserRepository : Repository
{
public override void Save()
{
Console.WriteLine("ユーザー情報の保存ログを出力しました");
}
}
この場合、実行結果は次のようになります。
C#ユーザー情報の保存ログを出力しました
親クラスのSaveメソッドに書かれていた「データを保存しました」という処理は実行されません。
つまり、overrideしたメソッドの中で親クラスの処理も必要な場合は、明示的にbase.メソッド名()を書く必要があります。
4-5. newで隠蔽したメソッドとoverrideの違い
C#では、overrideではなくnewを使って親クラスのメソッドを隠蔽することもできます。
C#class Parent
{
public void Show()
{
Console.WriteLine("親クラスのShow");
}
}
class Child : Parent
{
public new void Show()
{
Console.WriteLine("子クラスのShow");
}
}
newは、親クラスの同名メソッドを「隠す」ためのものです。
一方、overrideは親クラスのvirtualメソッドを「上書き」するためのものです。
違いを確認してみましょう。
C#class Parent
{
public virtual void Show()
{
Console.WriteLine("親クラスのShow");
}
}
class OverrideChild : Parent
{
public override void Show()
{
Console.WriteLine("overrideしたShow");
}
}
class NewChild : Parent
{
public new void Show()
{
Console.WriteLine("newで隠蔽したShow");
}
}
実行例です。
C#Parent a = new OverrideChild();
a.Show();
Parent b = new NewChild();
b.Show();
出力結果は次のようになります。
C#overrideしたShow
親クラスのShow
overrideの場合は、変数の型がParentでも実体がOverrideChildなら子クラス側のメソッドが呼ばれます。
一方、newで隠蔽した場合は、変数の型がParentなら親クラス側のメソッドが呼ばれます。
baseは、overrideしたメソッドの中で親クラスの処理を呼び出すときによく使われます。
5. baseコンストラクタとは?親クラスのコンストラクタを呼び出す方法
baseはメソッドやプロパティだけでなく、コンストラクタでも使います。
子クラスのコンストラクタから親クラスのコンストラクタを呼び出すには、: base(...)を使います。
5-1. コンストラクタで使うbase(...)の基本構文
コンストラクタでbaseを使う基本構文は次のとおりです。
C#class Child : Parent
{
public Child() : base()
{
}
}
引数を渡す場合は次のように書きます。
C#class Child : Parent
{
public Child(string name) : base(name)
{
}
}
注意点として、base(...)はコンストラクタ本体の中ではなく、コンストラクタ宣言の後ろに書きます。
C#public Child(string name) : base(name)
{
// 子クラス側の初期化処理
}
5-2. 子クラスのコンストラクタから親クラスのコンストラクタへ値を渡す
具体例を見てみましょう。
C#class Person
{
public string Name { get; }
public Person(string name)
{
Name = name;
Console.WriteLine("Personコンストラクタ");
}
}
class Employee : Person
{
public int EmployeeId { get; }
public Employee(string name, int employeeId) : base(name)
{
EmployeeId = employeeId;
Console.WriteLine("Employeeコンストラクタ");
}
}
実行例です。
C#Employee employee = new Employee("田中", 1001);
Console.WriteLine(employee.Name);
Console.WriteLine(employee.EmployeeId);
出力結果は次のようになります。
C#Personコンストラクタ
Employeeコンストラクタ
田中
1001
Employeeクラスのコンストラクタで受け取ったnameを、: base(name)によって親クラスPersonのコンストラクタへ渡しています。
5-3. 親クラスに引数なしコンストラクタがある場合の動き
親クラスに引数なしコンストラクタがある場合、子クラス側で明示的に: base()を書かなくても、自動的に親クラスの引数なしコンストラクタが呼び出されます。
C#class Parent
{
public Parent()
{
Console.WriteLine("Parentコンストラクタ");
}
}
class Child : Parent
{
public Child()
{
Console.WriteLine("Childコンストラクタ");
}
}
実行例です。
C#Child child = new Child();
出力結果は次のとおりです。
C#Parentコンストラクタ
Childコンストラクタ
このコードは、実質的には次のように書いた場合と同じです。
C#class Child : Parent
{
public Child() : base()
{
Console.WriteLine("Childコンストラクタ");
}
}
5-4. 親クラスに引数付きコンストラクタしかない場合の注意点
親クラスに引数付きコンストラクタしかない場合、子クラスでは必ずbase(...)で親クラスのコンストラクタを呼び出す必要があります。
C#class Parent
{
public Parent(string name)
{
Console.WriteLine($"Parent: {name}");
}
}
class Child : Parent
{
public Child(string name) : base(name)
{
Console.WriteLine("Childコンストラクタ");
}
}
もし: base(name)を書かないと、C#は親クラスの引数なしコンストラクタを呼び出そうとします。
しかし、親クラスに引数なしコンストラクタが存在しないため、コンパイルエラーになります。
C#class Child : Parent
{
public Child(string name)
{
// エラー
}
}
親クラスに引数付きコンストラクタしかない場合は、子クラス側で適切な値を渡す必要があると覚えておきましょう。
5-5. コンストラクタの実行順序は親クラスから子クラス
C#のコンストラクタは、親クラスから子クラスの順に実行されます。
C#class Parent
{
public Parent()
{
Console.WriteLine("1. Parentコンストラクタ");
}
}
class Child : Parent
{
public Child()
{
Console.WriteLine("2. Childコンストラクタ");
}
}
実行例です。
C#Child child = new Child();
出力結果は次のようになります。
C#1. Parentコンストラクタ
2. Childコンストラクタ
子クラスのオブジェクトを作るときでも、まず親クラスの初期化が行われ、その後に子クラスの初期化が行われます。
これは、子クラスが親クラスの機能を土台として成り立っているためです。
5-6. 複数のコンストラクタがある場合のbase(...)の使い分け
親クラスに複数のコンストラクタがある場合、子クラス側から呼び出すコンストラクタをbase(...)の引数で選べます。
C#class Person
{
public string Name { get; }
public int Age { get; }
public Person()
{
Name = "未設定";
Age = 0;
}
public Person(string name)
{
Name = name;
Age = 0;
}
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
class Student : Person
{
public string SchoolName { get; }
public Student() : base()
{
SchoolName = "未設定";
}
public Student(string name) : base(name)
{
SchoolName = "未設定";
}
public Student(string name, int age, string schoolName) : base(name, age)
{
SchoolName = schoolName;
}
}
このように、子クラスのコンストラクタごとに、親クラスのどのコンストラクタを呼び出すかを変えられます。
6. thisとbaseの違いを比較して理解する
C#では、baseと似たキーワードとしてthisがあります。
thisとbaseはどちらもクラスの中で使いますが、指している対象が異なります。
6-1. thisは自分自身のクラス、baseは親クラスを指す
thisは現在のインスタンス、つまり自分自身を指します。
baseは現在のクラスの親クラス部分を指します。
C#class Parent
{
public string Message { get; set; } = "親";
}
class Child : Parent
{
public new string Message { get; set; } = "子";
public void Show()
{
Console.WriteLine(this.Message);
Console.WriteLine(base.Message);
}
}
出力結果は次のようになります。
C#子
親
this.Messageは子クラスのMessageを指し、base.Messageは親クラスのMessageを指します。
6-2. this.メンバーとbase.メンバーの使い分け
thisは、自分自身のフィールドやプロパティを明示するときに使います。
C#class Person
{
private string name;
public Person(string name)
{
this.name = name;
}
}
この例では、引数のnameとフィールドのnameを区別するためにthis.nameと書いています。
一方、baseは親クラスのメンバーを明示するときに使います。
C#class Parent
{
protected void Initialize()
{
Console.WriteLine("親クラスの初期化");
}
}
class Child : Parent
{
public void Setup()
{
base.Initialize();
Console.WriteLine("子クラスの初期化");
}
}
thisは自分自身、baseは親クラスと考えると使い分けやすくなります。
6-3. this(...)とbase(...)によるコンストラクタ呼び出しの違い
コンストラクタでは、this(...)とbase(...)という形でも使われます。
this(...)は、同じクラス内の別のコンストラクタを呼び出します。
C#class Person
{
public string Name { get; }
public int Age { get; }
public Person(string name) : this(name, 0)
{
}
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
この例では、Person(string name)からPerson(string name, int age)を呼び出しています。
一方、base(...)は親クラスのコンストラクタを呼び出します。
C#class Person
{
public Person(string name)
{
Console.WriteLine($"Name: {name}");
}
}
class Employee : Person
{
public Employee(string name) : base(name)
{
}
}
this(...)は同じクラス内、base(...)は親クラスという違いがあります。
6-4. this(...)とbase(...)は同じコンストラクタで同時に使えない
C#では、1つのコンストラクタでthis(...)とbase(...)を同時に書くことはできません。
C#class Child : Parent
{
// public Child() : this("default"), base()
// {
// }
// エラー
}
コンストラクタ初期化子として指定できるのは、this(...)またはbase(...)のどちらか一方です。
ただし、this(...)で同じクラスの別コンストラクタを呼び出し、その呼び出し先のコンストラクタでbase(...)を使うことはできます。
C#class Parent
{
public Parent(string name)
{
Console.WriteLine(name);
}
}
class Child : Parent
{
public Child() : this("default")
{
}
public Child(string name) : base(name)
{
}
}
この場合、Child()からChild(string name)が呼ばれ、さらにChild(string name)から親クラスのParent(string name)が呼ばれます。
7. baseを使うときによくあるエラーと対処法
baseは便利なキーワードですが、使い方を間違えるとコンパイルエラーになります。
ここでは、C#初心者がつまずきやすいbase関連のエラーと対処法を紹介します。
7-1. staticメソッド内でbaseを使おうとしてエラーになる
baseはインスタンスに関係するキーワードです。そのため、staticメソッドの中では使えません。
C#class Parent
{
public void Show()
{
Console.WriteLine("Parent");
}
}
class Child : Parent
{
public static void Test()
{
// base.Show(); // エラー
}
}
staticメソッドはインスタンスに属さないため、親クラスのインスタンスメンバーをbaseで呼び出せません。
対処法としては、staticを外してインスタンスメソッドにします。
C#class Child : Parent
{
public void Test()
{
base.Show();
}
}
または、設計上staticである必要がある場合は、インスタンスを作成して呼び出す方法を検討します。
7-2. privateメンバーをbaseで呼び出そうとしてエラーになる
親クラスのprivateメンバーは、子クラスからbaseを使っても呼び出せません。
C#class Parent
{
private void Secret()
{
Console.WriteLine("秘密の処理");
}
}
class Child : Parent
{
public void Test()
{
// base.Secret(); // エラー
}
}
privateは、そのクラスの内部だけで使えるメンバーです。
子クラスから呼び出したい場合は、親クラス側でprotectedやpublicに変更します。
C#class Parent
{
protected void Secret()
{
Console.WriteLine("子クラスから呼び出せる処理");
}
}
class Child : Parent
{
public void Test()
{
base.Secret();
}
}
ただし、何でもprotectedやpublicにすればよいわけではありません。外部に公開すべきでない処理は、カプセル化を意識して設計することが大切です。
7-3. baseコンストラクタをコンストラクタ本体の中で呼ぼうとしてエラーになる
親クラスのコンストラクタを呼び出すbase(...)は、コンストラクタ本体の中には書けません。
C#class Parent
{
public Parent(string name)
{
}
}
class Child : Parent
{
public Child(string name)
{
// base(name); // エラー
}
}
正しくは、コンストラクタ宣言の後ろに書きます。
C#class Child : Parent
{
public Child(string name) : base(name)
{
}
}
base(...)は、コンストラクタの実行前に親クラスの初期化を行うためのものです。そのため、コンストラクタ本体の中ではなく、コンストラクタ初期化子として指定します。
7-4. 親クラスに適切なコンストラクタがなくてエラーになる
親クラスに引数なしコンストラクタがない場合、子クラスでbase(...)を書かないとエラーになります。
C#class Parent
{
public Parent(string name)
{
}
}
class Child : Parent
{
public Child()
{
// エラー
}
}
この場合、C#は暗黙的にbase()を呼び出そうとします。
しかし、ParentクラスにはParent()が存在しないため、コンパイルエラーになります。
対処法は、親クラスのコンストラクタに合う形でbase(...)を指定することです。
C#class Child : Parent
{
public Child() : base("default")
{
}
}
または、親クラスに引数なしコンストラクタを追加します。
C#class Parent
{
public Parent()
{
}
public Parent(string name)
{
}
}
7-5. baseを変数のように扱おうとしてエラーになる
baseはキーワードであり、変数ではありません。
そのため、次のような使い方はできません。
C#class Child : Parent
{
public void Test()
{
// var parent = base; // エラー
}
}
baseは、親クラスのメンバーにアクセスするための構文として使います。
C#base.SomeMethod();
base.SomeProperty = value;
親クラス型の変数として扱いたい場合は、通常はthisを親クラス型に代入できます。
C#class Parent
{
}
class Child : Parent
{
public void Test()
{
Parent parent = this;
}
}
ただし、この場合もbaseを変数として使っているわけではありません。子クラスのインスタンスは親クラス型として扱える、という継承の性質を利用しています。
8. 実践サンプルで学ぶC#のbaseの使い方
ここからは、実践的なサンプルコードを使ってbaseの使い方を確認します。
単純な例だけでなく、業務アプリや例外クラスの継承でもbaseはよく使われます。
8-1. AnimalクラスとDogクラスで学ぶ基本例
まずは、AnimalクラスとDogクラスを使った基本例です。
C#class Animal
{
public string Name { get; }
public Animal(string name)
{
Name = name;
}
public virtual void Speak()
{
Console.WriteLine($"{Name}が鳴きます");
}
}
class Dog : Animal
{
public Dog(string name) : base(name)
{
}
public override void Speak()
{
base.Speak();
Console.WriteLine($"{Name}がワンと鳴きます");
}
}
実行例です。
C#Dog dog = new Dog("ポチ");
dog.Speak();
出力結果は次のようになります。
C#ポチが鳴きます
ポチがワンと鳴きます
このサンプルでは、2つのbaseが登場します。
1つ目は、コンストラクタの: base(name)です。
C#public Dog(string name) : base(name)
{
}
これは、親クラスAnimalのコンストラクタにnameを渡しています。
2つ目は、メソッド内のbase.Speak()です。
C#base.Speak();
これは、親クラスAnimalのSpeakメソッドを呼び出しています。
8-2. EmployeeクラスとManagerクラスで学ぶ業務アプリ風の例
次に、社員情報を扱う業務アプリ風の例です。
C#class Employee
{
public string Name { get; }
public int Salary { get; }
public Employee(string name, int salary)
{
Name = name;
Salary = salary;
}
public virtual void ShowInfo()
{
Console.WriteLine($"名前: {Name}");
Console.WriteLine($"給与: {Salary}円");
}
}
class Manager : Employee
{
public string Department { get; }
public Manager(string name, int salary, string department)
: base(name, salary)
{
Department = department;
}
public override void ShowInfo()
{
base.ShowInfo();
Console.WriteLine($"部署: {Department}");
}
}
実行例です。
C#Manager manager = new Manager("佐藤", 500000, "営業部");
manager.ShowInfo();
出力結果は次のとおりです。
C#名前: 佐藤
給与: 500000円
部署: 営業部
Managerクラスは、EmployeeクラスのNameとSalaryを引き継ぎ、さらにDepartmentを追加しています。
base(name, salary)で親クラスの初期化を行い、base.ShowInfo()で親クラスの表示処理を再利用しています。
8-3. 例外クラスを継承してbase(message)を使う例
C#では、独自の例外クラスを作るときにもbaseを使うことがあります。
C#class ValidationException : Exception
{
public ValidationException(string message) : base(message)
{
}
}
この例では、Exceptionクラスのコンストラクタにmessageを渡しています。
使い方は次のとおりです。
C#throw new ValidationException("入力値が不正です");
例外を受け取る側では、Messageプロパティからメッセージを取得できます。
C#try
{
throw new ValidationException("入力値が不正です");
}
catch (ValidationException ex)
{
Console.WriteLine(ex.Message);
}
出力結果は次のようになります。
C#入力値が不正です
独自例外クラスでは、親クラスであるExceptionの機能を活かすために、base(message)を使うのが一般的です。
8-4. 親クラスの初期化と子クラスの追加処理を組み合わせる例
最後に、親クラスの初期化と子クラス独自の初期化を組み合わせる例を見てみましょう。
C#class Account
{
public string AccountId { get; }
public Account(string accountId)
{
AccountId = accountId;
Console.WriteLine("アカウントを初期化しました");
}
public virtual void Login()
{
Console.WriteLine($"アカウントID: {AccountId}でログインしました");
}
}
class AdminAccount : Account
{
public string Role { get; }
public AdminAccount(string accountId, string role)
: base(accountId)
{
Role = role;
Console.WriteLine("管理者アカウントを初期化しました");
}
public override void Login()
{
base.Login();
Console.WriteLine($"権限: {Role}");
}
}
実行例です。
C#AdminAccount admin = new AdminAccount("admin001", "Administrator");
admin.Login();
出力結果は次のとおりです。
C#アカウントを初期化しました
管理者アカウントを初期化しました
アカウントID: admin001でログインしました
権限: Administrator
この例では、オブジェクト生成時に親クラスのコンストラクタが先に実行され、その後に子クラスのコンストラクタが実行されます。
また、Loginメソッドでは親クラスのログイン処理をbase.Login()で呼び出し、その後に管理者権限の情報を表示しています。
9. C#のbaseに関するよくある質問
ここでは、C#のbaseについて初心者が疑問に感じやすいポイントをQ&A形式で解説します。
9-1. baseは必ず書く必要がある?
baseは常に書く必要があるわけではありません。
親クラスのメソッドやプロパティを普通に使うだけであれば、baseを書かなくてもアクセスできます。
C#class Parent
{
public void Show()
{
Console.WriteLine("Show");
}
}
class Child : Parent
{
public void Test()
{
Show();
}
}
このように、親クラスのShowメソッドはそのまま呼び出せます。
ただし、次のような場合はbaseを使う意味があります。
子クラスで同名メンバーがあり、親クラス側を明示したい場合
overrideしたメソッドの中で親クラスの処理も呼び出したい場合子クラスのコンストラクタから親クラスの引数付きコンストラクタを呼び出したい場合
必要な場面で使えば十分です。
9-2. 親クラスのコンストラクタは必ず呼ばれる?
はい。子クラスのインスタンスを作成するとき、親クラスのコンストラクタは必ず呼ばれます。
子クラスは親クラスを土台としているため、まず親クラス部分を初期化する必要があります。
親クラスに引数なしコンストラクタがある場合は、自動的に呼び出されます。
C#class Parent
{
public Parent()
{
Console.WriteLine("Parent");
}
}
class Child : Parent
{
public Child()
{
Console.WriteLine("Child");
}
}
この場合、Childを生成すると、Parentのコンストラクタが先に実行されます。
親クラスに引数付きコンストラクタしかない場合は、子クラス側でbase(...)を使って明示的に呼び出します。
9-3. C#のbaseはJavaのsuperと同じ?
C#のbaseは、Javaのsuperに近い役割を持っています。
Javaでは、親クラスのメソッドやコンストラクタを呼び出すときにsuperを使います。
C#では、その役割をbaseが担います。
C#の例です。
C#public Child(string name) : base(name)
{
}
public override void Show()
{
base.Show();
}
Javaの例では、次のようなイメージです。
Javapublic Child(String name) {
super(name);
}
@Override
public void show() {
super.show();
}
役割は似ていますが、構文は言語によって異なります。
C#ではコンストラクタのbase(...)を、コンストラクタ本体の中ではなく宣言部分の後ろに書く点に注意しましょう。
9-4. interfaceに対してbaseは使える?
基本的に、baseは親クラスのメンバーを呼び出すために使います。
インターフェイスはクラスのように実装を継承するものではないため、通常の意味で「interfaceに対してbaseを使う」とは考えません。
C#interface IWorker
{
void Work();
}
class Employee : IWorker
{
public void Work()
{
Console.WriteLine("働きます");
}
}
この例では、EmployeeクラスがIWorkerインターフェイスを実装していますが、base.Work()のようにインターフェイスのメソッドを呼び出すわけではありません。
ただし、C#にはインターフェイスの既定実装など高度な機能もあります。初心者の段階では、baseは「親クラスを呼び出すもの」と理解しておくとよいでしょう。
9-5. baseで親のprivateメンバーを呼び出せないのはなぜ?
privateメンバーは、そのクラスの内部だけで使うためのものだからです。
継承している子クラスであっても、親クラスのprivateメンバーには直接アクセスできません。
C#class Parent
{
private void Secret()
{
Console.WriteLine("秘密");
}
}
class Child : Parent
{
public void Test()
{
// base.Secret(); // エラー
}
}
これは、クラスの内部実装を外部から守るための仕組みです。
子クラスに公開したい処理がある場合は、親クラス側でprotectedにします。
C#class Parent
{
protected void Secret()
{
Console.WriteLine("子クラスから使える処理");
}
}
privateは完全に内部用、protectedは派生クラス向け、publicは外部にも公開するもの、と考えるとわかりやすいです。
9-6. baseと継承を使いすぎるとよくない?
baseや継承は便利ですが、使いすぎるとコードの見通しが悪くなることがあります。
継承階層が深くなると、ある処理がどのクラスで定義され、どの順番で呼び出されているのかを追いにくくなります。
また、親クラスの変更が子クラスに影響しやすくなるため、保守が難しくなる場合もあります。
継承を使うときは、「本当に親子関係として自然か」を考えることが大切です。
たとえば、DogはAnimalの一種なので継承として自然です。
C#class Dog : Animal
{
}
一方、「機能を使い回したいだけ」の場合は、継承ではなく、別クラスをフィールドとして持つ「委譲」や、インターフェイスを使う設計の方が適していることもあります。
baseは便利なキーワードですが、継承設計が適切な場合に使うことが重要です。
まとめ
C#のbaseは、子クラスから親クラスのメンバーを呼び出すためのキーワードです。
主な使い方は、親クラスのメソッドやプロパティを呼び出すこと、そして親クラスのコンストラクタを呼び出すことです。
C#base.MethodName();
base.PropertyName;
public Child(string name) : base(name)
{
}
overrideと組み合わせると、親クラスの処理を残したまま子クラス独自の処理を追加できます。
C#public override void Show()
{
base.Show();
Console.WriteLine("子クラスの追加処理");
}
また、コンストラクタでbase(...)を使うと、子クラスの生成時に親クラスへ必要な値を渡して初期化できます。
C#public Employee(string name, int id) : base(name)
{
Id = id;
}
thisが自分自身のクラスを指すのに対し、baseは親クラスを指します。
baseを理解すると、C#の継承、オーバーライド、コンストラクタの仕組みがよりわかりやすくなります。
初心者のうちは、まず次の3点を押さえておきましょう。
base.メソッド名()で親クラスのメソッドを呼び出せるbase.プロパティ名で親クラスのプロパティにアクセスできる: base(...)で親クラスのコンストラクタを呼び出せる
C#でオブジェクト指向プログラミングを学ぶうえで、baseは避けて通れない重要なキーワードです。継承とセットで理解し、実際にサンプルコードを書きながら使い方に慣れていきましょう。

