【C#】拡張メソッド

組込み型をはじめ、他人の作った型に「こういうメソッドが定義してあればいいのになぁ」と思ったことはないでしょうか?
その要望に(仮想的ですが)応えるのが拡張メソッドです。

例えば、Listへの項目追加を行うAddメソッドは戻り値がvoidですので、追加宣言はそれのみで終了してしまうことになります。
しかし、「Add後のListを返してほしい」パターンもあり得ます。連続追加や他のメソッドの引数表示内でのAddなどの可能性ですね。
本来の表記上は

List<int> intList = new List<int>();
intList.Add(1);
intList.Add(2);

のようなコードになります。

ここで、Addした上で本体を返却してくれる拡張メソッドを定義してみます。以下のような静的クラスをコードに追加します。

static public class ExtendMethods
{
    static public List<T> SelfReturnAdd<T>(this List<T> arg, T item)
    { 
        arg.Add(item);
        return arg;
    }
}

こうすると、このExtendMethodsクラスを参照可能な範囲では以下のように記述できます。

List<int> intList = new List<int>();
intList.SelfReturnAdd(1).SelfReturnAdd(2);

"SelfReturnAdd()"はAddしたあとのListをそのまま返却してくれますので、連続してAdd処理が可能になります。

さて、これが拡張メソッドなのですが、上記の通り呼び出しの際には
(インスタンス).(メソッド名)という、インスタンスの型内で実装した場合と全く同じ呼び出し方ができています。これが拡張メソッドの特徴です。
もし拡張メソッドの記述法を使用しない場合は

ExtendMethods.SelfReturnAdd(ExtendMethods.SelfReturnAdd(intList, 1), 2);

という書き方となり、非常に見づらいです。
このように、拡張メソッドを用いることで直観的で見やすいコードを書くことができます。

この様に便利な拡張メソッドですが、定義する場合の決まりごとがあります。

  1.  必ず静的(static)クラス内で静的メソッドとして宣言する。

  2.  引数には(インスタンス).(メソッド名)としたいインスタンスを加え、その型の前に"this"を付ける。

  3.  ”this"と"out"は同時に設定できない。

1, 2 は例で挙げたコードの通りです。
3 については"this out List<T> arg"といったような記載ができないということですね。一方で"ref"であれば併用可能なようです。

拡張メソッドは自作の型にも付与できますが、自作の型の場合には普通にメソッド追加をした方が良いことも多いです。
通常のメソッドに対する拡張メソッドのデメリットとしては

  1.  private / protectedなどの非公開メンバーにはアクセスできない

  2. メソッドの記載が散逸しやすい

といった点が挙げられます。
1. については、拡張メソッドはあくまで「拡張メソッドの記述法を使用しない場合」の記述を短縮した記述をしているだけなので、あくまで引数としてインスタンスを受け取った場合にアクセス可能なメンバーにしかアクセスできません。
2. については、単純にファイル/名前空間がバラバラでも拡張メソッドは成立するため、どこで書いたのかわからなくなったり、別のプロジェクトで使用しようとした際に参照漏れを起こしたりするリスクがある、という点ですね。

以上、拡張メソッドについての説明を終了致します。


いいなと思ったら応援しよう!