C#でInterfaceに拡張メソッドを追加してデフォルト実装してみる

みなさんこんにちは!ひろぽんです!

今回は拡張メソッドでinterfaceのデフォルト実装する方法について書いていきたいと思います。

正直C#8.0からInterfaceのデフォルト実装って標準でできるようになっているのですが、現場の関係でC#8.0使えない人はinterfaceのデフォルト実装をどうすればいいのか?ってなると思います。

今回はそういう人に向けてInterfaceのデフォルト実装を拡張メソッドを用いて実装する方法について書いていきます!

目次

そもそも拡張メソッドとは?

💡 Interface 全般の使い方は別記事 Interfaceの使いどころ で書いてます。

そもそも拡張メソッドとは何ぞや?というはなしなのですが、結論から言うとstaticな静的メソッドをインスタンスメソッドと同じような感じで呼び出せる仕組みです。

例えばintの数字に2乗にした数字を返すというSquareという関数を拡張メソッドとして持たせると下記のようになります。

    public static class IntExtenssionMethod
    {
        public static int Square(this int num)
        {
            return num * num;
        }
    }

この関数を実際に使うと下記のようになります。

    class Program
    {
        static void Main(string[] args)
        {
            int num = 2;

        // 2の2乗
        Console.WriteLine(num.Square());
        // 2の3乗
        Console.WriteLine(num.Square().Square());
        // 2の4乗
        Console.WriteLine(num.Square().Square().Square());

        Console.WriteLine();

    }
}</code></pre></div>

これを実行して。

あたかもint型が初めから持っていた関数のように扱えますよね?

では例をもう一つ。

文字列の前後に*○○*という感じでデコレーションする関数を拡張メソッドとして実装すると下記のようになります。

    public static class StringExtenssionMethod
    {
        public static string Decoration(this string str)
        {
            return $@"*{str}*";
        }
    }

これを実行すると!

            string str = "Hello World";
            Console.WriteLine(str.Decoration());
            Console.ReadLine();

このように拡張メソッドとはClassの定義の外で定義しているのにも関わらず、あたかも初めからそのクラスにあったというような感じで振舞えるのです!

これをInterfaceに応用するとデフォルト実装が実現できます!

Interfaceに拡張メソッドを入れるとデフォルト実装できる

ではこの記事の本題であるInterfaceに拡張メソッドでデフォルト実装をする方法について書いていきたいと思います。

まずは下記のようなInterfaceがあります。

namespace InterfaceExtenssionMethod
{
    public interface IAnimal
    {
        string RoarSound { get; }
    }
}

このInterfaceは動物を表しており、RoarSoundという動物の鳴き声のプロパティだけを持っています。

そしてRoarという拡張メソッドを用意してこの関数を実行すれば鳴き声を出力するようにします。

    public static class AnimalExtenssionMethod
    {
        public static void Roar(this IAnimal animal)
        {
            Console.WriteLine(animal.RoarSound);
        }
    }

そしてIAnimalを継承したDogクラスとCatクラスを実装します。

    public class Dog : IAnimal
    {
        public string RoarSound => "わんわん";
    }

public class Cat : IAnimal
{
    public string RoarSound =&gt; &quot;にゃーにゃー&quot;;
}</code></pre></div>

するとどうでしょうか。InterfaceにRoarという関数が元から合ったように振舞います。

            var dog = new Dog();
            dog.Roar();
            Console.ReadLine();

違う動物クラスを入れて実行してみたら、その動物クラスが持っている鳴き声で鳴きます。

            var dog = new Dog();
            var cat = new Cat();
            dog.Roar();
            cat.Roar();
            Console.ReadLine();

以上Interfaceに拡張メソッドでデフォルト実装をする方法でした!

今回のサンプルコードはGithubに上げています!

下記リポジトリに「InterfaceExtenssionMethod」というフォルダ名で今回のサンプルコードを上げています!

GitHub
GitHub - HayashiyamaHiroshi/blog-source-demo Contribute to HayashiyamaHiroshi/blog-source-demo development by creating an account on GitHub.

💡 補足: Interface 拡張メソッド / default member でハマる3点

俺もこの拡張メソッド + Interface default member、 業務でハマってきたところを3つ並べておきます。

① 拡張メソッドが Interface に「実装」されたと勘違い

拡張メソッドは あくまで static method。 Interface 経由で動的呼び出し (IFoo.Bar()) はできない。 ポリモーフィズム使いたいなら C# 8+ default interface members。

② default interface members の競合 (C# 8+)

複数 Interface が同名 default メソッド持つと、 実装クラスでどちらが呼ばれるか曖昧。 明示キャスト ((IFoo)obj).Method() で解決。 業務系では基本使わない方が安全。

③ this パラメータ忘れで通常 static method 扱い

public static int Length(this string s)this 忘れ → 拡張メソッドにならず通常 static として扱われる。 s.Length() 構文使えない。

❓ よくある質問

Q1. 拡張メソッド vs default interface members どっち?

A. 既存 Interface 拡張 = 拡張メソッド新規 Interface = default members。 default members は破壊的変更を避けるための機能 (既存実装クラスを修正不要)。

Q2. 拡張メソッドの可視性は?

A. public static class + public static method。 namespace が using されてないと呼べないので、 ユーティリティ名前空間 (例: MyApp.Extensions) でまとめる。

Q3. LINQ メソッドは拡張メソッド?

A. はい。 IEnumerable<T> に対する Where / Select / Count 全部拡張メソッド。 System.Linq を using すると使える。

Q4. 拡張メソッドのパフォーマンスは?

A. 通常 static 呼び出しと同等。 オーバヘッド無し。 ただし LINQ は遅延評価なので、 ToList() するまで実行されない。

Q5. 拡張メソッドのテストは?

A. MyExtensions.Bar(s) で直接呼べる。 mocking はできない (static なので)。 mock 必要なら依存性注入で Wrapper クラス作る。

📚 関連記事

この記事が気に入ったら
いいねしてね!

どんどんシェア待ってるぜ!!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次