白猫のメモ帳

C#とかJavaとかJavaScriptとかHTMLとか機械学習とか。

Java使いがC#を勉強する その⑥ 拡張メソッド

こんばんは。

何やら今日は妙に温かかったですね。
そして明日は寒いらしいですね。体調に気をつけましょう。

さて、C#には拡張メソッドというデンジャラスな機能があります。
今回はこれを見ていきます。

拡張ってどういうことだ


通常、あるクラスにメソッドを追加したい場合には、
そのクラスに直接メソッドを書きます。

public class Hoge {}

public class Hoge
{
  public void Piyo()
  {
    Console.WriteLine("ぴよ");
  }
}

しかし、そのクラスが自分の持ち物ではない場合、
むやみやたらにクラスに変更を加えるわけにはいきません。

そのため、対象のクラスを継承した派生クラスを作ります。
そうすると派生クラスでは親クラスのメソッドと自クラスのメソッドの両方が使えます。

public class HogeEx : Hoge
{
  public void Piyo()
  {
    Console.WriteLine("ぴよ");
  }
}

が、元のクラスに変更を加えたくない(再コンパイルしたくない)けれど、
メソッドを追加したいという場合に拡張メソッドという機能を使うことができます。

public class ExtensionMethods
{
  public void Piyo(this Hoge hoge)
  {
     Console.WriteLine("ぴよ");

  }
}

つまり、クラスにメソッドを外から「拡張」するわけですね。

 

使うべきじゃない?

 

なかなかに便利そうな拡張メソッドですが、いいことばかりではありません。

①Objectクラスにメソッドを拡張した場合、すべての派生クラスにメソッドが追加される
②拡張メソッドの定義はどこで行ってもよいため、メソッドの定義が分散する
③クラスのメソッドとシグネチャが一致した場合、拡張メソッドは優先されない
④拡張メソッドの定義が複数読み込まれた場合、どちらも利用できない

などなど、様々な問題があります。

③では言語仕様の変更やライブラリのアップデートに伴ってメソッドが追加され、
既存のコードの挙動が変わる可能性があります。

④でも同じ拡張メソッドが追加された場合、コードが正常に動作しません。


一般的に有用に利用されるのは以下の2つの場面が多いようです。

インタフェースへのメソッド追加

前回の記事でも触れましたが、基本的にインタフェースにメソッドの実装を書くことはできません。
そのため、多重継承のないC#ではMix-Inができないわけです。

しかし、拡張メソッドを使うとインタフェースにもメソッドを追加することができます。
前回の記事のコードそのままです。

public interface IPiyo {}

public static class Util
{
  static string Piyo(this IPiyo piyo) {
    return "ぴよ";
  }
}

便利ですけど、そのインタフェースに書きたくないですか?
はて。

enumへのメソッド追加

C#では列挙型にメソッドを実装することができません。
それどころか値を持たせることすらできません。

enum Direction { North, East, South, West }

static class DirectionExtension {

    static string Name(this Direction direction) {
        string name = "";
        switch (direction) {
            case Direction.North:
                name = "北";
                break;
            case Direction.East:
                name = "東";
                break;
            case Direction.South:
                name = "南";
                break;
            case Direction.West:
                name = "西";
                break;
        }
        return name;
    }
}

一応、拡張メソッドを使うとメソッドが作れます。
値は渡せませんが。
うわー死んでしまう。

なんかこれなら自分でタイプセーフEnum作りたい。

sealed class Direction {

    public string Name { get; private set; }

    private Direction(string name) {
        this.Name = name;
    }

    public static readonly Direction North = new Direction("北");
    public static readonly Direction East = new Direction("東");
    public static readonly Direction South = new Direction("南");
    public static readonly Direction West = new Direction("西");
}


ご利用は計画的に


個人で作るアプリでは標準ライブラリのクラスなどに
好き勝手に拡張メソッドを作ると楽しいのかもしれません。

が、チームで開発するときにはあまりお勧めできなそうです。
用法用量を守って正しく使いましょう。