[C#] クラス継承とポリモーフィズム

##概要
- 本稿では，C#初学者向けに「クラスの継承」について~~しょうもない~~具体的な実装例を交えながら解説を行う．

##継承とは
- あるクラスから機能や性質（＝メンバ）を受け継いで新しいクラスを作ること．
- 「人間 ⊃ 社員」のように，包括関係にあるものに対し，「社員は人間を継承する」，「社員は人間から派生する」などと表現し，ここでの「人間」のことを「基底クラス（base class）」 または「スーパークラス（super class）」と呼び， 「社員」のことを「派生クラス（derived class）」 または「サブクラス（sub class）」と呼ぶ．

###基底クラスの実装
- 「人間」を表す`Person`クラスに対し，氏名プロパティ`string Name`, 生年月日プロパティ`DateTime Birth`，個人番号プロパティ`string MyNumber`，`readonly`な年齢プロパティ`int Age`，年齢計算用の静的メソッド`protected static int GetAge(DateTime birth, DateTime? today = null)`を与える．
- ここで，`protected`は，クラス内またはそのクラスを継承する派生クラス内からのみアクセス可能なレベルである．

```c#
    /// <summary>
    /// 人間を表します．
    /// </summary>
    public class Person
    {
        /// <summary>
        /// 氏名
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 生年月日
        /// </summary>
        public DateTime Birth { get; set; }

        /// <summary>
        /// 個人番号
        /// </summary>
        public string MyNumber { get; set; }

        /// <summary>
        /// 年齢
        /// </summary>
        public int Age => GetAge(this.Birth);

        /// <summary>
        /// 年齢を計算します．
        /// </summary>
        /// <param name="birth">生年月日</param>
        /// <param name="today">計算の基準となる日付 ※指定しない場合はシステム日付</param>
        /// <returns>年齢</returns>
        protected static int GetAge(DateTime birth, DateTime? today = null)
        {
            if (!today.HasValue) today = DateTime.Today;
            var age = today.Value.Year - birth.Year;
            if (birth.AddYears(age) > today.Value) age--;
            return age;
        }
    }
```

###派生クラスの実装
- 「社員」を表す`Employee`クラスを作成し，`Person`クラスを継承させる．
    - クラス宣言部分を`class Derived : Base`の形式で記載．
- `Employee`クラスに対し，社員番号プロパティ`int Id`, 入社日プロパティ`DateTime Entry`，部署プロパティ`string Department`，`readonly`な勤続年数プロパティ`int ServiceYears`を与える．
- ここで，`Employee`クラスは`Person`クラスを継承しているため，基底クラスである`Person`クラスの年齢計算用の静的メソッド`protected static int GetAge(...)`を利用できる．

```c#
    /// <summary>
    /// 社員を表します．
    /// </summary>
    public class Employee : Person
    {
        /// <summary>
        /// 社員番号
        /// </summary>
        public int Id { get; private set; }

        /// <summary>
        /// 入社日
        /// </summary>
        public DateTime Entry { get; private set; }

        /// <summary>
        /// 部署
        /// </summary>
        public string Department { get; private set; }

        /// <summary>
        /// 勤続年数
        /// </summary>
        public int ServiceYears => GetAge(this.Entry);
    }
```

###テスト

- `Employee`クラスは`Person`クラスを継承しているので，基底クラスである`Person`のメンバを持つことが分かる．

```c#
var employee = new Employee();
employee.Name = "夜神　月";
employee.Birth = new DateTime(1986, 2, 28);
employee.MyNumber = "000000000000";
employee.Id = 8536110;
employee.Entry = new DateTime(2009, 4, 1);
employee.Department = "警察庁情報通信局情報管理課";
var age = employee.Age;
var serviceYears = employee.ServiceYears;
```

##アクセシビリティ
- 変数やメソッドに対して，どこからアクセスできるかという制限のレベル．
- ここでの「アセンブリ」とは，「プロジェクト」「exe」「dll」などを指す．

| アクセシビリティ|レベル |
|:--|:--|
|public|どこからでもアクセス可能|
|protected |クラス内部と派生クラスの内部からのみアクセス可能|
|internal|同一アセンブリ内のクラスからのみアクセス可能|
|protected internal|同一アセンブリ内のクラス内部，または派生クラスの内部からのみアクセス可能|
|private protected|同一アセンブリ内のクラス内部，かつ派生クラスの内部からのみアクセス可能|
|private|クラス内部からのみアクセス可能|

##コンストラクタ
- 派生クラスのインスタンスが生成されるとき，まず基底クラスのコンストラクタが呼び出され，その後派生クラスのコンストラクタが呼び出される．
- 派生クラスで，基底クラスの引数つきコンストラクタを呼び出すためには， 明示的に基底クラスのコンストラクタを呼び出す必要がある．

###基底クラスのコンストラクタ
```c#
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="name">氏名</param>
/// <param name="birth">生年月日</param>
/// <param name="myNumber">個人番号</param>
public Person(string name, DateTime birth, string myNumber)
    => (this.Name, this.Birth, this.MyNumber) = (name, birth, myNumber)
```

###派生クラスのコンストラクタ
- コンストラクタ定義部分に`public Derived(...) : base(...) { ... }`の形式で記載．

```c#
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="name">氏名</param>
/// <param name="birth">生年月日</param>
/// <param name="myNumber">個人番号</param>
/// <param name="id">社員番号</param>
/// <param name="entry">入社日</param>
/// <param name="department">部署</param>
public Employee(string name, DateTime birth, string myNumber, int id, DateTime entry, string department) : base(name, birth, myNumber)
    => (this.Id, this.Entry, this.Department) = (id, entry, department);
```

##継承の禁止(`sealed`)
- クラス宣言部分に`sealed`修飾子を付与した場合，継承不可となる．

##基底クラスのメンバを上書き(`override`)する
- 基底クラスで定義されたメンバを，派生クラスで独自機能に上書きすること．

###基底クラスの実装
- メソッドやプロパティに対して`virtual`修飾子を付与することで，派生クラスでの`override`が可能となる．

```c#
/// <summary>
/// 方言を表す基底クラスです．
/// </summary>
public class Dialect
{
    /// <summary>
    /// 方言を取得します．
    /// </summary>
    protected virtual string GetDialect() => "サイゼリアへ行きませんか？";

    /// <summary>
    /// 方言を発言します．
    /// </summary>
    public void Say() => Console.WriteLine($"{this.GetType().Name}：{this.GetDialect()}");
}
```

###派生クラスの実装
- `override`修飾子を付与することで，基底クラスの`virtual`なメソッドやプロパティを上書き可能となる．
- 基底クラス側の処理を呼び出す場合は，`base.Member`の形式で記載する．

```c#
/// <summary>
/// 標準語を表す派生クラスです．※本来は継承しなくて良い
/// </summary>
public class StandardLanguage : Dialect
{
    // 本来は override しなくて良い
    protected override string GetDialect() => base.GetDialect();
}
```

```c#
/// <summary>
/// 関西弁を表す派生クラスです．
/// </summary>
public class KansaiDialect : Dialect
{
    protected override string GetDialect() => "サイゼ行かへん？";
}
```

```c#
/// <summary>
/// 関東弁を表す派生クラスです．
/// </summary>
public class KantoDialect : Dialect
{
    protected override string GetDialect() => "ゼリア行こうじゃん？";
}
```

###テスト


- 派生クラスのインスタンスは基底クラスのインスタンスとして扱うことができる．[^2]
- 基底クラスでメソッドを呼び出したときでも，CLRによってオブジェクトの実行時の型が検索され，そのメソッドの派生クラス版が実行される．[^3]

```c#
var dialects = new List<Dialect>(){ new Dialect(), 
                                    new StandardLanguage(), 
                                    new KansaiDialect(), 
                                    new KantoDialect() };
dialects.ForEach(d => d.Say());
```

###実行結果

```
Dialect：サイゼリアへ行きませんか？
StandardLanguage：サイゼリアへ行きませんか？
KansaiDialect：サイゼ行かへん？
KantoDialect：ゼリア行こうじゃん？
```

##上書き(`override`)の禁止
- 基底クラスで`override`を禁止するには，`virtual`を不要しなければよい．
- 派生クラスで基底クラスから`override`したメンバを，さらに派生させたクラスで`override`禁止するには，`sealed`修飾子を付与する．

```c#
/// <summary>
/// 讃岐弁を表す派生クラスです．
/// </summary>
public class SanukiDialect : Dialect
{
    sealed protected override string GetDialect() => "おなか起きた";
}
```

```c#
/// <summary>
/// ヒンディー語を表す(讃岐弁の)派生クラスです．
/// </summary>
public class HindiDialect : SanukiDialect
{
    // エラー：継承されたメンバ'SanukiDialect.GetDialect()'はシールドされているため，オーバーライドできません．
    protected override string GetDialect() => "पेट से भरा";
}
```

##`System.Object`のメソッドを`override`する
- C#ではすべてのクラスまたは構造体が暗黙的に`Object`クラスを継承する．
- そのため，C# のすべてのオブジェクトが以下のメソッドを得る．
    - `ToString()`：現在のオブジェクトを表す文字列を返す．
    - `Equals()`：2 つのオブジェクトインスタンスが等しいかどうかを判断する．
- ここでは例として，図鑑番号と種族名の属性を持つ`Pokomon`クラスを実装し，`System.Windows.Forms.Form`上で`System.Windows.Forms.ListBox`へのアイテム追加を考える．

###クラスとフォームの実装
```c#:クラス[Pokomon]の実装
/// <summary>
/// ポ〇モンを表します．
/// </summary>
public class Pokomon
{
    /// <summary>
    /// 図鑑番号
    /// </summary>
    public int Id { get; private set; }

    /// <summary>
    /// 種別名
    /// </summary>
    public string Name { get; private set; }

    /// <summary>
    /// コンストラクタ
    /// </summary>
    /// <param name="id">図鑑番号</param>
    /// <param name="name">種別名</param>
    public Pokomon(int id, string name) => (this.Id, this.Name) = (id, name);
}
```

```c#:Formの実装
// サンプルポコモンを追加
this.ListBox.Items.AddRange(new Pokomon[] { new Pokomon(0, "ィ゛ゃゾ┛A"),
                                            new Pokomon(6, "アネﾞデパミﾞ"),
                                            new Pokomon(135, "ベアビヲ９"),
                                            new Pokomon(253, "ダメタマゴ"),
                                            new Pokomon(255, "けつばん")});
```

###実行結果

> ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659824/287a99b8-c842-23ae-8745-b6f361269eea.png)

###`ToString()`を`override`する
- `ToString()`を`override`しない場合，既定の文字列変換が行われ，クラス名が表示される．
- `Pokomon.Name`プロパティを追加していけば表示は正せるが，追加アイテムが`string`型になってしまい取得時に都合が悪い．
- `ToString()`を`override`することで，指定した文字列を表示可能である．
- `ToString()`の`override`では`Empty`または`null`文字列を返さないようにする必要がある．また，例外をスローしない．

####実装例
```c#:Pokomon.cs
/// <summary>
/// 現在のインスタンスを表す文字列を取得します．
/// </summary>
public override string ToString() => this.Name;
```

####実行結果
>![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/659824/34ccc587-8d96-c59d-0a49-58f7e2b60cbe.png)

###`Equals(Object)`を`override`する
- 既定の`Equals(Object)`では**参照の等価**を判断するため，以下のように判断される．

####実行結果

```c#:Form1.cs
var pokomon1 = new Pokomon(6, "アネﾞデパミﾞ");
var pokomon2 = new Pokomon(6, "アネﾞデパミﾞ");
var judge1 = pokomon1.Equals(pokomon1);             //実行結果：true
var judge2 = pokomon1.Equals(pokomon2);             //実行結果：false
var judge3 = this.ListBox.Items.Contains(pokomon1); //実行結果：false
```

- ここで，`pokomon`とは図鑑番号によって一意に決定されることとし，`Equals(Object)`を`override`して図鑑番号での等価性を確保する．
- `Equals(Object)`の実装では，例外をスローすることはできず，常に値を返す必要がある．例えば`Object`が`null`の場合，`ArgumentNullException`を`throw`するのではなく，`false`を返す必要がある．
- `Equals(Object)`を`override`する型は，ハッシュテーブルを正しく機能させるため，`GetHashCode()`もオーバーライドする必要がある．
- 値型の場合，さらに下記の実装が必要となるが，本稿では割愛する（[Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.object.equals?view=netcore-3.1)を参照）．
    - `IEquatable<T>`インターフェイス
    - 等値演算子`==`のオーバーロード

####実装例

```c#:Pokomon.cs
/// <summary>
/// 指定したオブジェクトが，現在のオブジェクトと等しいかどうか判断します．
/// </summary>
public override bool Equals(object obj) => (obj as Pokomon)?.Id == this.Id;

/// <summary>
/// 既定のハッシュ関数として機能します．
/// </summary>
public override int GetHashCode() => this.Id;
```

####実行結果

```c#:Form1.cs
var pokomon1 = new Pokomon(6, "アネﾞデパミﾞ");
var pokomon2 = new Pokomon(6, "アネﾞデパミﾞ");
var judge1 = pokomon1.Equals(pokomon1);                 //実行結果：true
var judge2 = pokomon1.Equals(pokomon2);                 //実行結果：true
var judge3 = this.ListBox.Items.Contains(pokomon1);     //実行結果：true
```

##基底クラスのメンバを隠蔽(`new`)する

- 派生クラスで，基底クラスと同じシグネチャのメンバを再定義すること．[^1]
- メンバの実体が 1 つに収束するわけではなく，基底クラスと派生クラスで同じメンバが重複して存在するようになる．
- `new`修飾子を付与することで，基底クラスのメソッドやプロパティを隠蔽して再定義できる．このメンバは`virtual`でなくてもよい．

###実装例
- `sealed`でないクラス`Light_Yagami`に対し，「私はキラです」と自己紹介を行う`virtual`でないプロパティを与える．

```c#
/// <summary>
/// キラを表します．
/// </summary>
public class Light_Yagami
{
    /// <summary>
    /// 自己紹介します．
    /// </summary>
    public string SelfIntroduction => "私はキラです";
}
```
- `Light_Yagami`を継承した`L_Lawliet`クラスで，自分がキラであることを隠蔽するため，自己紹介プロパティを`new`して再定義し，「私はLです」などと宣うことにする．

```c#
/// <summary>
/// (2代目)Lを表します．
/// </summary>
public class L_Lawliet : Light_Yagami
{
    /// <summary>
    /// 自己紹介します．
    /// </summary>
    public new string SelfIntroduction => "私はLです";
}
```

###実行結果
```c#
var K = new Light_Yagami(); 
var L = new L_Lawliet(); 
Console.WriteLine(K.SelfIntroduction);      // 実行結果：私はキラです
Console.WriteLine(L.SelfIntroduction);      // 実行結果：私はLです
```

##上書き(`override`)と隠蔽(`new`)の相違

- `new`はフィールドの型に応じて呼び出しメソッドが決められる．
- `override`はインスタンスの型に応じて呼び出しメソッドが決められる．

###`override`の動作

####実装例

```c#
/// <summary>
/// キラを表します．
/// </summary>
public class Light_Yagami
{
    public virtual string SelfIntroduction => "私はキラです";
}
```

```c#
/// <summary>
/// (2代目)Lを表します．
/// </summary>
public class L_Lawliet : Light_Yagami
{
    public override string SelfIntroduction => "私はLです";
}
```

####実行結果

```c#:DeathNoteTester.cs
Light_Yagami K = new Light_Yagami();        // フィールド：Light_Yagami，インスタンス：Light_Yagami
L_Lawliet L1 = new L_Lawliet();             // フィールド：L_Lawliet，インスタンス：L_Lawliet
Light_Yagami L2 = new L_Lawliet();          // フィールド：Light_Yagami，インスタンス：L_Lawliet

Console.WriteLine(K.SelfIntroduction);      // 実行結果：私はキラです
Console.WriteLine(L1.SelfIntroduction);     // 実行結果：私はLです
Console.WriteLine(L2.SelfIntroduction);     // 実行結果：私はLです
```

###`new`の動作

####実装例

```c#:Light_Yagami.cs
/// <summary>
/// キラを表します．
/// </summary>
public class Light_Yagami
{
    public string SelfIntroduction => "私はキラです";
}
```

```c#:L_Lawliet.cs
/// <summary>
/// (2代目)Lを表します．
/// </summary>
public class L_Lawliet : Light_Yagami
{
    public new string SelfIntroduction => "私はLです";
}
```

####実行結果

```c#
Light_Yagami K = new Light_Yagami();        // フィールド：Light_Yagami，インスタンス：Light_Yagami
L_Lawliet L1 = new L_Lawliet();             // フィールド：L_Lawliet，インスタンス：L_Lawliet
Light_Yagami L2 = new L_Lawliet();          // フィールド：Light_Yagami，インスタンス：L_Lawliet

Console.WriteLine(K.SelfIntroduction);      // 実行結果：私はキラです
Console.WriteLine(L1.SelfIntroduction);     // 実行結果：私はLです
Console.WriteLine(L2.SelfIntroduction);     // 実行結果：私はキラです
```

##抽象クラス

- 継承して派生クラスでインスタンス生成して使うことを前提としたクラス．
    - クラスに`abstract`修飾子を付与して宣言する．
- 抽象クラス自身は実体を持たず，インスタンス化できない．
- 基底クラスでメソッドの意味だけを定義して実装を持たず，派生クラス上での実装を強制する**抽象メソッド**を定義できる．
    - メソッドに`abstract`修飾子を付与して宣言する．

###抽象クラスの実装

```c#
/// <summary>
/// 方言を表す抽象クラスです．
/// </summary>
public abstract class Dialect
{
    /// <summary>
    /// 方言を取得します． ※抽象メソッド：実装を持たず，派生クラスでの実装を強制する．
    /// </summary>
    protected abstract string GetDialect();

    /// <summary>
    /// 方言を発言します．
    /// </summary>
    public void Say() => Console.WriteLine($"{this.GetType().Name}：{this.GetDialect()}");
}
```

###派生クラスの実装

```c#
/// <summary>
/// 標準語を表す派生クラスです．
/// </summary>
public class StandardLanguage : Dialect
{
    protected override string GetDialect() => "サイゼリアへ行きませんか？";
}
```

```c#
/// <summary>
/// 関西弁を表す派生クラスです．
/// </summary>
public class KansaiDialect : Dialect
{
    protected override string GetDialect() => "サイゼ行かへん？";
}
```

```c#
/// <summary>
/// 関東弁を表す派生クラスです．
/// </summary>
public class KantoDialect : Dialect
{
    protected override string GetDialect() => "ゼリア行こうじゃん？";
}
```

###テスト

```c#

var dialects = new List<Dialect>(){ new StandardLanguage(), new KansaiDialect(), new KantoDialect() };
dialects.ForEach(d => d.Say());
```

###実行結果

```
StandardLanguage：サイゼリアへ行きませんか？
KansaiDialect：サイゼ行かへん？
KantoDialect：ゼリア行こうじゃん？
```

##ポリモーフィズム
- クラスメンバがシグネチャに応じて異なる複数の実装を持ち，呼び出し時に使い分けできることで，操作が統一的であること．[^1]
- 抽象クラスではメンバの意味だけを与え，派生クラスでの実装を強要できることから，統一的なクラス設計が可能である．
- ポリモーフィズムではインターフェース継承が必須となるが，これについては別記事で記載予定である．
- デザインパターンとしての継承の使い方については，下記記事が非常に参考になる．
    - [君の継承の使い方は間違っている - Qiita](https://qiita.com/tonluqclml/items/c0110098722763caa556)

##参考
- [継承 - C# によるプログラミング入門 | ++C++; // 未確認飛行 C](https://ufcpp.net/study/csharp/oo_inherit.html)
- [実装の隠蔽 - C# によるプログラミング入門 | ++C++; // 未確認飛行 C](https://ufcpp.net/study/csharp/oo_conceal.html#level)
- [Object.ToString メソッド (System) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.object.tostring?view=netcore-3.1)
- [Object.Equals メソッド (System) | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/api/system.object.equals?view=netcore-3.1)
- [Override キーワードと New キーワードを使用する場合について - C# プログラミング ガイド | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/knowing-when-to-use-override-and-new-keywords)
- [ポリモーフィズム - C# プログラミング ガイド | Microsoft Docs](https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/polymorphism)
- [君の継承の使い方は間違っている - Qiita](https://qiita.com/tonluqclml/items/c0110098722763caa556)
- [夜神月 - Wikipedia](https://ja.wikipedia.org/wiki/%E5%A4%9C%E7%A5%9E%E6%9C%88)
- [L (DEATH NOTE) - Wikipedia](https://ja.wikipedia.org/wiki/L_(DEATH_NOTE))
- [サイゼリヤトップページ｜サイゼリヤ](https://www.saizeriya.co.jp/)
- [バグポケモン一覧 - ポケモンWiki](https://wiki.ポケモン.com/wiki/%E3%83%90%E3%82%B0%E3%83%9D%E3%82%B1%E3%83%A2%E3%83%B3%E4%B8%80%E8%A6%A7)

[^1]: シグネチャ：メソッドの名前，引数の数やデータ型，返値の型などの組み合わせのこと．
[^2]: 派生クラスから基底クラスへの型変換をアップキャストといい，派生クラスが基底ークラスのすべてのメンバを保証できるため暗黙的な変換が可能である．その逆のダウンキャスト（基底クラスから派生クラスへの型変換）では，基底クラスが派生クラスのすべてのメンバーを保証できないため．明示的な型変換が必要となる．
[^3]: CLR[Common Language Runtime、共通言語ランタイム]：.NET Frameworkなどを実行するための動作環境．