← SEARCHABLE ARCHIVE
C# · 2026-07-25

C# 基礎からマスターまで — 第4回

継承、インタフェース、ジェネリックに役割を分ける

INTERACTIVE READING MODE →

本ブログ作成文は、講義を目指して製作されました。 また、作成された文章の情報も間違っている可能性がありますので、ご注意ください。

複数のクラスが同じ動作を提供する必要がある場合は、継承とインタフェースを使用できます。 共通点を結ぶことより、どの契約を外部に見せるかがより重要である。

インターフェースで役割を表現する

public interface IDamageable
{
    bool IsAlive { get; }
    void TakeDamage(int amount);
}

public sealed class Monster : IDamageable
{
    public int Health { get; private set; } = 100;
    public bool IsAlive => Health > 0;

    public void TakeDamage(int amount)
    {
        Health = Math.Max(0, Health - amount);
    }
}

攻撃コードはMonster全体ではなくIDamageableだけ知ればよい。 後で訓練用かかしや破壊可能なボックスを追加しても同じコードを使うことができる。

継承はis-a関係に使用します

public abstract class Skill
{
    public string Name { get; }

    protected Skill(string name) => Name = name;
    public abstract void Execute(IDamageable target);
}

public sealed class FireSkill : Skill
{
    public FireSkill() : base("Fire") { }

    public override void Execute(IDamageable target)
    {
        target.TakeDamage(30);
    }
}

継承は、基底クラスの状態と規則まで一緒に継承される。 単にコード数行を再利用しようとする深い継承構造を作成すると、親の変更がすべての子に影響を与えます。

ジェネリックでタイプの安全性を維持する

public sealed class Registry<T> where T : class
{
    private readonly Dictionary<int, T> values = new();

    public void Add(int id, T value) => values.Add(id, value);

    public T? Find(int id)
    {
        return values.TryGetValue(id, out T? value) ? value : null;
    }
}

Registry<Monster>とRegistry<Skill>は同じ保存規則を使用しますが、互いの値を混在させることはできません。 制約であるwhereは、Tが提供すべき性質をコンパイラに伝えます。

インタフェースは役割、継承は共通基盤、ジェネリックは型が異なる同じ規則を表現する。 3つの機能を一度に多く書くより最小の契約から作り、必要に応じて拡張するほうが安全だ。