明示的実装(Explicit Implementation)とは

C#では、クラスや構造体がインターフェイスを実装する際に、「明示的実装」という方法を使用できます。明示的実装では、インターフェイスのメンバーをクラスや構造体の外から直接使用できないようにし、インターフェイス型を介してのみ利用可能にします。


明示的実装を使用する場面

  1. 名前の競合を解消する場合
    • クラスが複数のインターフェイスを実装しており、それらが同じ名前のメンバーを持っている場合。
  2. カプセル化を強化する場合
    • クラスの内部で直接利用されることを防ぎ、インターフェイス型を介してのみ操作を許可する場合。

明示的実装の基本構文

public interface IExample
{
    void DoSomething();
}

public class ExampleClass : IExample
{
    // 明示的実装
    void IExample.DoSomething()
    {
        Console.WriteLine("DoSomethingは呼ばれた.");
    }
}

この例では、DoSomething メソッドは IExample 型を介してのみアクセス可能です。


使用例

名前の競合を解決する場合

public interface IFlyable
{
    void Move();
}

public interface ISwimmable
{
    void Move();
}

public class Animal : IFlyable, ISwimmable
{
    // IFlyable.Move の明示的実装
    void IFlyable.Move()
    {
        Console.WriteLine("飛ぶ...");
    }

    // ISwimmable.Move の明示的実装
    void ISwimmable.Move()
    {
        Console.WriteLine("泳ぐ...");
    }
}

// 使用例
Animal animal = new Animal();
// animal.Move(); // コンパイルエラー:直接呼び出せません。

IFlyable flyable = animal;
flyable.Move(); // "飛ぶ..." と出力

ISwimmable swimmable = animal;
swimmable.Move(); // "泳ぐ..." と出力

カプセル化を強化する場合

明示的実装を使うことで、特定のメンバーをインターフェイス型を介してのみ操作可能にできます。

public interface IDataProcessor
{
    void ProcessData();
}

public class DataProcessor : IDataProcessor
{
    void IDataProcessor.ProcessData()
    {
        Console.WriteLine("処理されたデータ.");
    }

    // クラス独自のメソッド
    public void LogProcessing()
    {
        Console.WriteLine("Log: 処理開始.");
    }
}

// 使用例
DataProcessor processor = new DataProcessor();
// processor.ProcessData(); // コンパイルエラー

IDataProcessor iProcessor = processor;
iProcessor.ProcessData(); // "処理されたデータ." と出力
processor.LogProcessing(); // "Log: 処理開始." と出力

明示的実装の制限と注意点

  1. アクセス修飾子を指定できない
    • 明示的実装されたメンバーは常に public 扱いとなりますが、実装クラスの型からはアクセスできません。
  2. インターフェイス型を明示する必要がある
    • 明示的実装されたメンバーにアクセスするには、インターフェイス型としてキャストする必要があります。
  3. 初期化の制限
    • 明示的実装されたプロパティに初期値を設定する場合、C# 7.0以降でのみサポートされています。

実践課題

以下のコードを完成させて、IRunnableIStoppable を実装するクラスを作成してください。それぞれのメソッドは明示的実装を用いて動作を分ける必要があります。

public interface IRunnable
{
    void Execute();
}

public interface IStoppable
{
    void Execute();
}

public class TaskManager : IRunnable, IStoppable
{
    // TODO: 明示的実装を用いて Execute メソッドをそれぞれ実装してください。
}

// 使用例
var taskManager = new TaskManager();

IRunnable runnable = taskManager;
runnable.Execute(); // "走る..." と出力

IStoppable stoppable = taskManager;
stoppable.Execute(); // "止まる..." と出力
public interface IRunnable
{
    void Execute();
}

public interface IStoppable
{
    void Execute();
}

public class TaskManager : IRunnable, IStoppable
{
    // IRunnable.Execute の明示的実装
    void IRunnable.Execute()
    {
        Console.WriteLine("Running...");
    }

    // IStoppable.Execute の明示的実装
    void IStoppable.Execute()
    {
        Console.WriteLine("Stopping...");
    }
}

// 使用例
public class Program
{
    public static void Main(string[] args)
    {
        var taskManager = new TaskManager();

        // インターフェイス型を介してアクセス
        IRunnable runnable = taskManager;
        runnable.Execute(); // "走る..." と出力

        IStoppable stoppable = taskManager;
        stoppable.Execute(); // "止まる..." と出力
    }
}

解説

  1. 明示的実装
    • IRunnable.Execute と IStoppable.Execute をそれぞれ明示的に実装することで、両方のインターフェイスの Execute メソッドを異なる動作にしています。
    • 明示的実装を使うことで、TaskManager 型としては Execute メソッドを直接呼び出せないように制限しています。
  2. インターフェイス型を介したアクセス
    • 実装されたメソッドにアクセスするには、オブジェクトを IRunnable または IStoppable 型にキャストします。

実行結果

走る...
止まる...

このように明示的実装を活用すると、インターフェイスごとに異なる動作を実現しつつ、クラスの外部で直接操作されるリスクを抑えることができます。


終わりに

明示的実装は、柔軟性とカプセル化を強化する強力な手法です。一方で、インターフェイス型を意識しなければならないため、適切な場面でのみ使用することが重要です。この資料をもとに、明示的実装の利点と制約を理解し、実践に活かしてください。

C#

Posted by hidepon