今回はクラスの継承についてです。
継承を使うと、元にするクラスの持つ機能(メンバー)を受け継いだ新しいクラスを作ることができます。
継承のもとになるクラスのことを基底クラス、
基底クラスのメンバーを継承するクラスのことを派生クラスといいます。
継承のやりかた
継承の構文はこんな感じです。
C#では1つのクラスしか継承できません。
class 派生クラス名 : 基底クラス名
{
// 派生クラスのメンバーの定義を書く
}
冒頭に書きましたが派生クラスは基底クラスのメンバーを受け継ぎます。
using System;
class Program
{
public static void Main()
{
// 派生クラスのインスタンスを作成
SampleClass sample = new SampleClass();
// 派生クラスは基底クラスのメンバーも持っている
sample.Field = 20;
sample.Method();
// 派生クラス独自のメンバーにアクセス
sample.SampleField = 30;
sample.SampleMethod();
}
}
// 基底クラス
class BaseClass
{
// フィールド
public int Field;
// メソッド
public void Method() => Console.WriteLine($"BaseClassのメソッドです。Fieldは{this.Field}");
}
// 派生クラス
class SampleClass : BaseClass
{
// 派生クラス独自のフィールド
public int SampleField;
// 派生クラス独自のメソッド
public void SampleMethod() {
// 派生クラス内からはbaseキーワードで基底クラスのメンバーにアクセスできる
Console.WriteLine($"SampleClassのSampleMethodです。Fieldは{base.Field}、SampleFieldは{this.SampleField}");
}
}
実行結果はこんな感じ。
BaseClassのメソッドです。Fieldは20
SampleClassのSampleMethodです。Fieldは20、SampleFieldは30
アクセシビリティ
派生クラスは基底クラスのメンバーを受け継ぐと書きましたが、基底クラスのメンバーのアクセシビリティによって受け継ぐことができるメンバーが決まります。
基底クラスメンバーの アクセシビリティ | 説明 |
---|---|
public | 継承可能 また、基底・派生クラスの内部、外部両方からのアクセスが可能 |
protected | 継承可能 基底・派生クラス内のみからアクセスが可能 |
private | 継承不可 基底クラス内のみからアクセスが可能 |
using System;
class Program
{
public static void Main()
{
// 派生クラスのインスタンスを作成
SampleClass sample = new SampleClass();
// クラス外部からアクセスできるのはpublicフィールド
sample.Field_public = 99;
}
}
// 基底クラス
class BaseClass
{
// publicなフィールド
public int Field_public;
// protectedなフィールド
protected int Field_protected;
// privateなフィールド
private int Field_private;
}
// 派生クラス
class SampleClass : BaseClass
{
// 派生クラス独自のメソッド
public void SampleMethod()
{
base.Field_public = 10;
base.Field_protected = 20;
// 基底クラスのprivateフィールドにはアクセスできない
//base.Field_private = 30;
}
}
コンストラクタ
基底クラスがコンストラクタを持っている場合、派生クラスのインスタンスを作成する際に基底クラスのコンストラクタが呼ばれます。
using System;
class Program
{
public static void Main()
{
// 派生クラスのインスタンスを作成
SampleClass sample = new SampleClass();
}
}
// 基底クラス
class BaseClass
{
// コンストラクタ
public BaseClass()
{
Console.WriteLine("BaseClassのコンストラクタが実行されました。");
}
}
// 派生クラス
class SampleClass : BaseClass { }
BaseClassのコンストラクタが実行されました。
派生クラスもコンストラクタを持っている場合、
基底クラスのコンストラクタ ⇒ 派生クラスのコンストラクタ の順番で呼ばれます。
using System;
class Program
{
public static void Main()
{
SampleClass sample = new SampleClass();
}
}
// 基底クラス
class BaseClass
{
// コンストラクタ
public BaseClass()
{
Console.WriteLine("BaseClassのコンストラクタが実行されました。");
}
}
// 派生クラス
class SampleClass : BaseClass
{
// コンストラクタ
public SampleClass()
{
Console.WriteLine("SampleClassのコンストラクタが実行されました。");
}
}
BaseClassのコンストラクタが実行されました。
SampleClassのコンストラクタが実行されました。
基底クラスが引数を必要とするコンストラクタを持っている場合、派生クラスも引数ありのコンストラクタが必要です。
こんな感じです。
派生クラスのコンストラクタ(引数) : base(基底クラスのコンストラクタに渡す引数) { }
using System;
class Program
{
public static void Main()
{
SampleClass sample = new SampleClass(10);
}
}
class BaseClass
{
// コンストラクタ
public BaseClass(int val)
{
Console.WriteLine($"BaseClassのコンストラクタが実行されました。val = {val}");
}
}
class SampleClass : BaseClass
{
// コンストラクタ
public SampleClass(int val) : base(val)
{
Console.WriteLine($"SampleClassのコンストラクタが実行されました。val = {val}");
}
}
BaseClassのコンストラクタが実行されました。val = 10
SampleClassのコンストラクタが実行されました。val = 10
派生クラスのコンストラクタの引数の数は、基底クラスと同じ必要はありません。
using System;
class Program
{
public static void Main()
{
SampleClass sample = new SampleClass(10, 20);
}
}
class BaseClass
{
// コンストラクタ
public BaseClass(int val)
{
Console.WriteLine($"BaseClassのコンストラクタが実行されました。val = {val}");
}
}
class SampleClass : BaseClass
{
// コンストラクタ
public SampleClass(int val1, int val2) : base(val1)
{
Console.WriteLine($"SampleClassのコンストラクタが実行されました。val = {val1}, val2 = {val2}");
}
}
BaseClassのコンストラクタが実行されました。val = 10
SampleClassのコンストラクタが実行されました。val = 10, val2 = 20
基底クラスのメンバーを上書きする
基底クラスと同じ名前のメンバーを派生クラスに定義すると、基底クラスのメンバーを上書きすることができます。
基底クラス、派生クラスそれぞれのインスタンスでそれぞれのメソッドが呼ばれるようになります。
using System;
// 基底クラス
class BaseClass
{
public int Field;
public void Method() => Console.WriteLine($"BaseClassのメソッドです。Fieldは{this.Field}");
}
// 派生クラス
class SampleClass : BaseClass
{
public string Field;
public void Method() => Console.WriteLine($"SampleClassのメソッドです。Fieldは{this.Field}");
}
class Program
{
public static void Main()
{
// 基底クラス
BaseClass baseCls = new BaseClass() { Field = 10 };
baseCls.Method();
// 派生クラス
SampleClass sample = new SampleClass() { Field = "abc" };
sample.Method();
}
}
処理結果はこんな感じ。
BaseClassのメソッドです。Fieldは10
SampleClassのメソッドです。Fieldはabc
ワーニングが気になる人は、newを付けると消えます。
class SampleClass : BaseClass
{
new public string Field;
new public void Method() => Console.WriteLine($"SampleClassのメソッドです。Fieldは{this.Field}");
}
継承する前提のクラス(abstract:抽象クラス)
abstractキーワードを付けるとそのクラスはインスタンスを作成することが出来なくなり、そのクラスを継承した派生クラスを定義して利用することになります。
abstractを付けて継承を強制するクラスのことを抽象クラスといいます。
基底クラスをインスタンス化する予定がない場合に付けておくといいです。
抽象クラスのメンバーにもabstractキーワードを付けて実装を強制することができます。
付けることのできるメンバーは、
- プロパティ
- インデクサー
- イベント
です。
abstractキーワードが付いたメンバーを実装するにはoverrideキーワードを付けます。
using System;
class Program
{
public static void Main()
{
// 派生クラスのインスタンスを作成
SampleClass sample = new SampleClass();
// 公開されている(publicな)メソッドを実行する
sample.Method_public();
}
}
// 抽象クラス
abstract class BaseClass
{
// publicな抽象メソッド
public abstract void Method_public();
// protectedな抽象メソッド
protected abstract void Method_protected();
}
// 派生クラス
class SampleClass : BaseClass
{
// 抽象メンバーを実装する
public override void Method_public() => this.Method_protected();
protected override void Method_protected() => Console.WriteLine("Method_protectedが実行されました。");
}
継承を禁止するクラス(sealed)
sealedキーワードを付けるとそのクラスを基底クラスにして継承することが出来なくなります。
sealed class SampleClass
{
// このクラスから派生クラスを定義することはできない
}
sealedが付いたクラスを継承しようとするとエラーになります。
長くなってきたので一旦区切ります。
次は仮想メソッドというものを使った多態性(1つのオブジェクトが異なる動作をする)の話をしようと思います。
C# 記事まとめページに戻る(他のサンプルコードもこちら)
コメント