C#における明示的インターフェース実装と継承の挙動解説

C#で複数のインターフェースを実装しつつ抽象クラスを継承する際、メソッド名が衝突するときの解決方法を整理します。

前提となる型定義

public interface ILeft
{
    void Execute();
}

public interface IRight
{
    void Execute();
}

public abstract class BaseRunner
{
    public abstract void Execute();
}

パターン1:抽象クラスを継承+明示的実装を利用

public sealed class MultiRunner : BaseRunner, ILeft, IRight
{
    // BaseRunner.Execute() をオーバーライド
    public override void Execute()
    {
        Console.WriteLine("BaseRunner.Execute");
    }

    // ILeft.Execute() を明示的実装
    void ILeft.Execute()
    {
        Console.WriteLine("ILeft.Execute");
    }

    // IRight.Execute() は BaseRunner.Execute と同一視
    // 明示的実装を省略すると BaseRunner.Execute が呼ばれる
}

この構成では BaseRunner.ExecuteIRight.Execute も兼ねるため、IRight 用の明示的実装は不要です。

パターン2:抽象クラスを継承しない場合

public sealed class StandaloneRunner : ILeft, IRight
{
    // 通常の public メソッド(ILeft / IRight 両方に見える)
    public void Execute()
    {
        Console.WriteLine("StandaloneRunner.Execute");
    }

    // ILeft 専用の実装
    void ILeft.Execute()
    {
        Console.WriteLine("ILeft.Execute");
    }

    // IRight 専用の実装
    void IRight.Execute()
    {
        Console.WriteLine("IRight.Execute");
    }
}

抽象クラスを継承しない場合、override は使用できません。代わりに public 修飾子を付けたメソッドを用意することで、インターフェース実装のデフォルト実装として機能させられます。

呼び出し側での違い

var runner = new MultiRunner();

ILeft  asLeft   = runner;
IRight asRight  = runner;
BaseRunner baseRef = runner;

asLeft.Execute();   // "ILeft.Execute"
asRight.Execute();  // "BaseRunner.Execute"
baseRef.Execute();  // "BaseRunner.Execute"
runner.Execute();   // "BaseRunner.Execute"

注意点まとめ

  • 明示的インターフェース実装にはアクセス修飾子を付けられない(常に private)
  • 抽象クラスを継承している場合、同名メソッドは override で一本化できる
  • 継承していない場合、各インターフェースごとに明示的実装を書くか、共通の public メソッドを用意する
  • 呼び出し側が保持する型によって実行される実装が変わる

タグ: C# interface explicit-interface-implementation abstract-class method-resolution

6月17日 16:10 投稿