1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C#における抽象メソッド・仮想メソッド・インターフェースの備忘録

1
Last updated at Posted at 2026-04-23

はじめに

誤りについては優しく指摘してくださると嬉しいです。

早見表

抽象メソッド 仮想メソッド インターフェース
インスタンス化 × ×
デフォルト実装 × 〇必須 〇任意
オーバーライド 〇必須 △任意 ×各自で実装
プロパティ 共通 共通 各クラスで定義
フィールド変数 ×定数のみ
コンストラクタ ×
多重継承 × × 〇複数実装可
用途 特定の処理を強制
is-a関係
明確なデフォルト有
is-a関係
共通の振る舞いを持つ
状態を共有しない

抽象メソッド

abstractをつけることで抽象クラス・抽象メソッドを定義できる。
抽象クラスはインスタンスを生成できないという特徴があり、継承を前提として使われる。

abstractがついたメソッドはoverrideで継承先で再定義できる。
継承先で必ず実装したいメソッドがある場合などに有効。

抽象メソッド
public abstract class Animal // 抽象クラス
{
    public string _name = "名無し";          // フィールド:継承される
    public abstract string Sound { get; }   // プロパティ:実装を強制、デフォルト値不可

    public abstract void Speak();           // 実装を強制、デフォルト実装不要
    public void Sleep() => Console.WriteLine($"{_name}: zzz..."); // 非抽象メソッドも持てる
}

public class Dog : Animal
{
    public override string Sound { get; } = "ワン";
    public override void Speak()
        => Console.WriteLine($"{_name}: {Sound}");
}

public class Cat : Animal
{
    public override string Sound { get; } = "ニャー";
    public override void Speak()
        => Console.WriteLine($"{_name}: {Sound}!");
}

// 呼び出し
Animal d = new Dog { _name = "ポチ" };
Animal c = new Cat { _name = "ミケ" };

d.Speak();   // ポチ: ワン
c.Speak();   // ミケ: ニャー!
d.Sleep();   // ポチ: zzz... 

仮想メソッド

virtualをつけることで仮想メソッドを定義できる。
virtualがついたメソッドはoverrideで継承先で再定義できる。

仮想メソッドは代入される変数の型 (=の左側) ではなく、代入する側の型 (=の右側) に基づいて呼ばれる。
つまり仮想メソッドを使えば、同じメソッドを呼んだとしても格納されているインスタンスの型によって異なる動作を行う。

仮想メソッド
public class Animal
{
    public string _name = "名無し";           // フィールド:継承される
    public virtual string Sound { get; } = "...";  // プロパティ:オーバーライド可能

    public virtual void Speak()              // デフォルト実装あり
        => Console.WriteLine($"{_name}: {Sound}");
}

public class Dog : Animal
{
    public override string Sound { get; } = "ワン";
    // Speak()はオーバーライドしなくてもよい(基底クラスの実装が使われる)
}

public class Cat : Animal
{
    public string _name = "ミケ";            // 基底の_nameを隠蔽する
    public override string Sound { get; } = "ニャー";
    public override void Speak()            // 独自にオーバーライドも可能
        => Console.WriteLine($"{_name} {Sound}!");
}

// 呼び出し
Animal d = new Dog { _name = "ポチ" };
Animal c = new Cat();

d.Speak();  // ポチ: ワン
c.Speak();  // ミケ ニャー!

overrideされたメソッドはさらに派生クラスでoverride可能。
また、base.メソッド名()で親クラスの実装を呼び出せる。
ただし抽象メソッドをbase経由で呼ぶと実行時エラーになる。

base.メソッド名() で親クラスの実装を呼び出せる
public abstract class Animal
{
    public string _name = "名無し";
    public abstract string Sound { get; }

    public abstract void Speak();
    public virtual void Sleep()          // virtualなので base呼び出し可能
        => Console.WriteLine($"{_name}: zzz...");
}

public class Dog : Animal
{
    public override string Sound { get; } = "ワン";
    public override void Speak()
        => Console.WriteLine($"{_name}: {Sound}");
    public override void Sleep()
    {
        base.Sleep();        // Animal.Sleep() を呼び出す → "ポチ: zzz..."
        Console.WriteLine($"{_name}: むにゃむにゃ...");  // 追加の処理
    }
}

public class GoldenRetriever : Dog
{
    public override string Sound { get; } = "ワンワン";
    public override void Sleep()
    {
        base.Sleep();   // Dog.Sleep() を呼び出す(Animal.Sleep()ではない)
        Console.WriteLine($"{_name}: ぐーぐー...");
    }
}

// 呼び出し
Animal d = new Dog { _name = "ポチ" };
Animal g = new GoldenRetriever { _name = "ゴールド" };

d.Speak();  // ポチ: ワン
d.Sleep();  // ポチ: zzz...
            // ポチ: むにゃむにゃ...

g.Speak();  // ゴールド: ワンワン
g.Sleep();  // ゴールド: zzz...        ← base.Sleep()経由でAnimal.Sleep()が呼ばれる
            // ゴールド: むにゃむにゃ... ← base.Sleep()経由でDog.Sleep()の処理が呼ばれる
            // ゴールド: ぐーぐー...

インターフェース

クラスが実装する規約を定めたもの。
抽象メソッドのみを持つ抽象クラスのようなイメージ。

インターフェース
public interface IAnimal
{
    // フィールド変数は定義不可(定数のみ可)
    const string Category = "動物";         // 定数はOK
    
    string Name { get; set; }              // プロパティ:実装を強制(C#8以降はデフォルト実装も可)
    string Sound { get; }
    void Speak();             
    void Sleep() => Console.WriteLine($"{Name}: zzz...");
}

public class Dog : IAnimal
{
    public string Name { get; set; } = "名無し";
    public string Sound { get; } = "ワン";
    public void Speak()
        => Console.WriteLine($"{Name}: {Sound}");
}

public class Cat : IAnimal
{
    public string Name { get; set; } = "名無し";
    public string Sound { get; } = "ニャー";
    public void Speak()
        => Console.WriteLine($"{Name}: {Sound}!");
    public void Sleep()                          // デフォルト実装を独自にオーバーライド
        => Console.WriteLine($"{Name}: すやすや...");
}

// 呼び出し(複数インターフェイスを同じ型として扱える)
IAnimal d = new Dog { Name = "ポチ" };
IAnimal c = new Cat { Name = "ミケ" };

d.Speak();   // ポチ: ワン
c.Speak();   // ミケ: ニャー!
d.Sleep();   // ポチ: zzz...
c.Sleep();   // ミケ: すやすや...
Console.WriteLine(IAnimal.Category);  // 動物

// IAnimal型経由でないとSleep()は呼び出せない
Dog d2 = new Dog{ Name = "ポチ" };
d2.Sleep();  // エラー

インターフェース名をメンバー名に付与することで、クラスのインスタンスからは
直接アクセスできないが、インターフェース型経由ではアクセスできるような挙動にもできる。

// 明示的インターフェース実装
// インターフェース名.メンバー名 の形で実装する(アクセス修飾子は書けない)
public class Bird : IAnimal
{
    public string Name { get; set; } = "名無し";
    public string Sound { get; } = "チュン";
    void IAnimal.Speak()   // 明示的実装
        => Console.WriteLine($"{Name}: {Sound}");
}

Bird b = new Bird { Name = "ピー" };
// b.Speak();        // エラー:クラス型経由では呼べない
((IAnimal)b).Speak(); // OK:インターフェース型にキャストすれば呼べる

参考

1
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?