ひとりでのアプリ開発 - fineの備忘録 -

ひとりでアプリ開発をするなかで起こったことや学んだことを書き溜めていきます

C# - 抽象クラス、抽象メソッド、abstract

初めに

 C# でクラスの継承をする際、abstract 修飾子が使われることがあります。abstract 修飾子がつけられたクラスや関数を抽象クラス、抽象メソッドといいます。本記事では、abstract 修飾子についてまとめます。


前提知識

 抽象クラスや抽象メソッドの説明には、継承や仮想メソッドなどの概念が必要になります。よくわからない場合は、ぜひ下の記事からご覧ください。

(継承について書かれた記事)
fineworks-fine.hatenablog.com

(仮想メソッドについて書かれた記事)
fineworks-fine.hatenablog.com

抽象クラス、抽象メソッド

クラスの継承で起こる問題点

 以前、仮想メソッドの説明に、次のようなコードをお見せしました。

//基底クラス
public class Pokemon
{
    string name;
    string type;

    //コンストラクタ
    public Pokemon() { }

    //仮想メソッド
    public virtual string Name { get { return "名前"; } }
    public virtual string Type { get { return "タイプ"; } }
}

//派生クラス
public class Pikachu : Pokemon
{
    //コンストラクタ
    public Pikachu() { }
    //オーバーライド
    public override string Name
    {
        get
        {
            return "pikachu";
        }
    }

    public override string Type
    {
        get
        {
            return "でんき";
        }
    }
}

 このコードでは、Pikachu というクラスが Pokemon というクラスを継承しています。また、基底クラスにある Name や Type という仮想メソッドを派生クラスでオーバーライドしています。

 このようなクラスの継承では、次のような場合があります。

  • 基底クラスは継承だけできればよい
  • 派生クラスしかインスタンス化する必要がない

 実際に、先ほどのコードも各ポケモンの情報だけ取得できればよいので、基底クラス自体をインスタンス化して、基底クラスの情報を取得する必要はありません。むしろ、意味のないインスタンスが生成できてしまう状態はよろしくありません。

抽象クラス、抽象メソッドとは

 上記のような問題を解決するために、抽象クラス、抽象メソッドという機能を使います。

 抽象クラス(abstract class)とは、

インスタンスを作成できないクラス

のことです。

 また、次のような関数を抽象メソッド(abstract method)と呼びます。

実体を持たず、意味だけを定義した仮想メソッド。抽象クラス内でのみ定義可能で、必ず派生クラスでオーバーライドしなければならない。

抽象クラス、抽象メソッドの定義の仕方

 抽象クラスは基底クラスに abstract 修飾子をつけることで定義できます。

 また、抽象メソッドも基底クラス内の関数に abstract 修飾子をつけることで定義できます。なお、基底クラス内のプロパティも関数のため、abstract をつけることで、抽象化することができます。

//基底クラスに abstract 修飾子をつけて、抽象クラスを定義
public abstract class Pokemon
{
    string name;
    string type;

    public Pokemon() { }

    //abstract をつけて、プロパティの抽象化
    //基底クラスでは実体を持たないので、アクセサーのみ
    public abstract string Name { get; }
    public abstract string Type { get; }

    //抽象メソッドの定義
    //実態のない関数(中括弧 {} のない関数)にabstract をつけて定義
    public abstract void Ability();
}
抽象メソッド、抽象プロパティを定義する際の注意点

 抽象メソッドを定義するときには、次のような注意点があります。

(抽象メソッドを定義する際の注意点)

  • 抽象クラス内でしか定義できない
  • abstract をつけた時点で抽象化された仮想メソッドを意味するので、virtual をつけるとエラーになる
  • 派生クラスごとに定義をオーバーライドするので、static 修飾子はつけられない

fineworks-fine.hatenablog.com

 また、抽象プロパティを定義するときには、次のような注意点があります。

(抽象プロパティを定義する際の注意点)

  • 抽象プロパティはアクセサー(get, set)の指定のみ可能
  • 抽象プロパティのアクセサーと派生クラスのアクセサーが異なるとエラーになる

サンプル
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

public class DeriveSample : MonoBehaviour
{
    void Start()
    {
        Pokemon pika = new Pikachu();
        Pokedex(pika);
    }

    public static void Pokedex(Pokemon p)
    {
        Debug.Log($"Name:{p.Name}, Type:{p.Type}");
    }
}

//abstractをつけて、抽象クラスにした
public abstract class Pokemon
{
    string name;
    string type;

    public Pokemon() { }

    //プロパティの抽象化
    public abstract string Name { get; }
    public abstract string Type { get; }

    public abstract void Ability();
}

//派生クラス
//抽象メソッド, 抽象プロパティのオーバーライドをしないとエラーがでる
public class Pikachu : Pokemon
{
    public Pikachu() { }
    public override string Name
    {
        get
        {
            return "pikachu";
        }
    }

    public override string Type
    {
        get
        {
            return "でんき";
        }
    }

    public override void Ability()
    {
        //関数の中身
    }
}

(実行結果)

abstract について

 abstract は直訳では、「抽象、概要」を意味します。Microsoftが出しているドキュメントでは、「修飾対象の実装が不足しているか、不完全であることを示す」修飾子であるとされています。実際、派生クラスでオーバーライドされて完全な形になると考えると理解しやすいかもしれません。

最後に

  • 継承をする際、基底クラスのインスタンス化が不要な場合など問題がある
  • 解決方法として、abstract 修飾子をつけて、抽象クラスや抽象メソッドを使うこと
  • 抽象クラスはインスタンス化できない
  • 抽象メソッドは派生クラスでオーバーライドしなければならない
  • プロパティも抽象化可能。中身はアクセサー(get, set)のみ。