Unity/C# 抽象クラスと override 入門
1ファイル → 分割 → 図解 → 自動テストまで
- 1. TL;DR
- 2. 1. サンプルコード
- 3. 2. 使い分け早見(abstract / virtual / base)
- 4. 3. いますぐ動かす:1ファイル最小例(Unity)
- 5. 4. 実務向け:クラスごとに分割する
- 6. 5. 図解(PlantUML:クラス図/シーケンス図+テーマ)
- 7. 6. override と new の即理解ミニ対比
- 8. 7. sealed override の使いどころ
- 9. 8. Unity Test Framework(最小ログ検証)
- 10. 9. よくあるつまずきQ&A(Unity特有)
- 11. 10. 練習課題(5分でできる)
- 12. 11. 期待ログと成功スクショの目安
- 13. 12. まとめ
TL;DR
- 学べること:abstract / virtual / override、base 呼び出し、new との違い、sealed override、Unity Test Frameworkでのログ検証。
- 最小実行:CarTestBehaviour を空オブジェクトにアタッチ → 再生 → Consoleでログ確認。
- 使い分けの軸:子ごとに必ず別実装なら abstract/共通の基本実装があり差し替え可にしたいなら virtual。
- 実践:1ファイル版 → 分割版 → 図解(PlantUML) → 自動テスト → つまずきQ&A → 練習課題の順で理解を固める。
1. サンプルコード
// 車クラス(abstractなのでnew Car()するとエラー)
abstract public class Car
{
public virtual void Run()
{
Debug.Log("走るよ!");
}
public void Stop()
{
Debug.Log("止まるよ!");
}
}
// スポーツカー
public class SportCar : Car
{
public override void Run()
{
Debug.Log("超早く");
base.Run(); // 親クラスのメソッドを呼び出す
}
}
var sportCar = new SportCar();
sportCar.Run(); // 「超早く」「走るよ!」と出力される
sportCar.Stop(); // 「止まるよ!」と出力される
2. 使い分け早見(abstract / virtual / base)
- 子クラスごとに実装が必ず異なる → abstract(親は実装を持たない“契約”)
- 共通の既定動作があり、必要時に差し替え可能 → 親で virtual、子で override
- 親の処理も活かしたい → 子で base.メソッド() を呼ぶ
3. いますぐ動かす:1ファイル最小例(Unity)
注意:Unityでは MonoBehaviour を継承するクラスは「クラス名=ファイル名」です。
例:public class CarTestBehaviour : MonoBehaviour → ファイルは CarTestBehaviour.cs
ファイル名:CarTestBehaviour.cs
using UnityEngine;
abstract class Car
{
public virtual void Run()
{
Debug.Log("走るよ!");
}
public void Stop()
{
Debug.Log("止まるよ!");
}
}
class SportCar : Car
{
public override void Run()
{
Debug.Log("超早く");
base.Run();
}
}
public class CarTestBehaviour : MonoBehaviour
{
void Start()
{
var sportCar = new SportCar();
sportCar.Run(); // 「超早く」「走るよ!」
sportCar.Stop(); // 「止まるよ!」
}
}
手順:空のGameObjectを作成 → CarTestBehaviour をアタッチ → 再生 → Consoleに出力が出ればOK。
4. 実務向け:クラスごとに分割する
別ファイル化で見通しと再利用性が向上します。別ファイルから参照するクラスは public を付けるのが基本。
ファイル名:Car.cs
using UnityEngine;
public abstract class Car
{
public virtual void Run()
{
Debug.Log("走るよ!");
}
public void Stop()
{
Debug.Log("止まるよ!");
}
}
ファイル名:SportCar.cs
using UnityEngine;
public class SportCar : Car
{
public override void Run()
{
Debug.Log("超早く");
base.Run();
}
}
ファイル名:CarTestBehaviour.cs
using UnityEngine;
public class CarTestBehaviour : MonoBehaviour
{
void Start()
{
var sportCar = new SportCar();
sportCar.Run();
sportCar.Stop();
}
}
5. 図解(PlantUML:クラス図/シーケンス図+テーマ)


6. override と new の即理解ミニ対比
public class Car
{
public virtual void Run() { Debug.Log("Car.Run"); }
}
public class CityCar : Car
{
public new void Run() { Debug.Log("CityCar.Run (new)"); } // ← 隠蔽(overrideではない)
}
public class RacingCar : Car
{
public override void Run() { Debug.Log("RacingCar.Run (override)"); } // ← 正しい上書き
}
void Demo()
{
CityCar cc = new CityCar();
cc.Run(); // CityCar.Run (new)
Car asBase1 = cc;
asBase1.Run(); // Car.Run(親が呼ばれる:new は隠蔽)
Car asBase2 = new RacingCar();
asBase2.Run(); // RacingCar.Run(override は多態が効く)
}
要点:親型参照で常に子の実装を使いたいなら override を選ぶ。
7. sealed override の使いどころ
public class SportCar : Car
{
public sealed override void Run()
{
Debug.Log("SportCar 最終版");
base.Run();
}
}
// 以降の派生は Run を override不可(API設計・挙動保証に有効)
8. Unity Test Framework(最小ログ検証)
EditMode テストを新規作成して下記を追加。手動実行から自動検証へ。
using NUnit.Framework;
using UnityEngine;
public class CarSpec
{
[Test]
public void SportCar_Run_Stop_Logs()
{
string logs = "";
void Capture(string m, string s, LogType t) => logs += m + "\n";
Application.logMessageReceived += Capture;
var sc = new SportCar();
sc.Run();
sc.Stop();
Application.logMessageReceived -= Capture;
StringAssert.Contains("超早く", logs);
StringAssert.Contains("走るよ!", logs);
StringAssert.Contains("止まるよ!", logs);
}
}
9. よくあるつまずきQ&A(Unity特有)
Q. CarTestBehaviour をアタッチできません
A. クラス名=ファイル名になっているか確認(例:CarTestBehaviour.cs)。
Q. 別ファイルの Car が参照できません
A. public が付いているか、名前空間の衝突がないか確認。
Q. new Car() がコンパイルエラーになります
A. Car は abstract。new SportCar() のように具体クラスを生成。
Q. ログが多すぎてビルド負荷が心配
A. 編集時のみログを出したい場合は Conditional 属性で制御:
public static class Log
{
[System.Diagnostics.Conditional("UNITY_EDITOR")]
public static void Info(string msg) => Debug.Log(msg);
}
10. 練習課題(5分でできる)
- Truck : Car を追加し、Run() を override。base.Run() を呼ぶ版/呼ばない版を比較。
- Car に virtual void Honk() を追加し、各派生でクラクションの違いを実装。
- List<Car> に SportCar/Truck を混在させ、foreach で Run()/Stop() を一括呼び出し。
11. 期待ログと成功スクショの目安
期待ログ(Console)
超早く
走るよ!
止まるよ!
スクショのキャプション例
再生すると Console に上記3行が表示されれば成功です。
12. まとめ
- abstract:子に必須実装を課す“契約”。
- virtual / override:共通実装を配りつつ、必要時に差し替え。
- base:親の処理も活かすときに呼ぶ。
- new ではなく override:親型参照でも子の実装を使いたい場合。
- sealed override:上書きの終点を宣言してAPIの安定性を高める。
- テストで固める:Unity Test Frameworkでログ検証を自動化。
ディスカッション
コメント一覧
まだ、コメントがありません