実は次のポストのコンセプトを理解するための、ヤクの毛刈りなのですが、拡張メソッドについて調べてみました。
拡張メソッドは、既存のメソッドや、インターフェイスに対して、後付けでメソッドを生やせる仕組みです。 Ruby とかだと、普通にクラスを書けばそうなりますが、静的言語ではどうなるでしょう?
例えば次のようなクラスがあります。
public class Employee
{
public string Name { get; set; }
public Employee (string name)
{
this.Name = name;
}
}
拡張メソッドをつかって、メソッドを追加するように見せる
こいつに対して拡張メソッドを使って、メソッドを追加してみましょう。本体には手を入れずにです。呼び捨ては失礼なので「様」づけするメソッドを追加します。
static class EmployeeExtensions
{
public static string GetNameWithRespect(this Employee self)
{
return self.Name + "様";
}
}
ここで、のポイントは、static のクラスに、static のメソッドを書いて、(this 型 変数名)
の定義をしてあげるだけになります。実際これを呼び出すためのメソッドはこんな感じ
class Program
{
static void Main(string[] args)
{
var emp = new Employee("三太夫");
Console.WriteLine(emp.Name);
Console.WriteLine(emp.GetNameWithRespect());
Console.ReadLine();
}
}
実行結果は、
三太夫
三太夫様
完璧!想定通り。しかし、だまされてはいけません、これは、あくまで、static メソッドが、インスタンスメソッドのように見せかけているだけなので、private にはアクセスできませんし、Mock とかするときに、拡張メソッドだったらMockできません。(Mock の回でお話ししました)
だから、普通は自分がメンテしているクラスだったら、ちゃんとクラスにインスタンスメソッドを書くべきだと思います。じゃあ、どんな時に良いかというと、インターフェイスに対しても同じようにかけるということらしいです。いつも超絶お世話になっているブログによると、
var input = new[] { 8, 9, 10, 11, 12, 13 };
var output =
Extensions.Select(
Extensions.Where(
input,
x => x > 10),
x => x * x);
と、普通に呼び出したら、こうなるところを、こう書けるのがメリットとあります。
var input = new[] { 8, 9, 10, 11, 12, 13 };
var output = input
.Where(x => x > 10)
.Select(x => x * x);
これはカッケー。もしかすると、これ使うと、自分で Map や flatMap 書けるんじゃね?
オレオレ Map を実装する
というわけで、C# にない、Map を実装してみましょう。Ruby で大好きだったやつで、イテレーターに関数適用するメソッドです。ちなみに、C# だと、Linq を使えばできますが、そこはあえて実装してみましょう。
先ほどのメソッドに追加
static class EmployeeExtensions
{
public static string GetNameWithRespect(this Employee self)
{
return self.Name + "様";
}
public static IEnumerable<X> Map<T, X>(this IEnumerable<T> e, Func<T,X> f)
{
List<X> x = new List<X>();
foreach(var element in e)
{
x.Add(f(element));
}
return x;
}
}
単純に、先ほど学んだ 拡張メソッドに対して、引数に Function を渡すようにしています。もともとの型は、どういう型にかわるかわかりませんので、型を変えています。そして、単純に関数適用して、結果を戻しています。
こんなコードをかいてみました。
var list = new List<int> { 1, 2, 3, 4 };
var result = list.Map<int, int>(p => p + 1);
foreach (var e in result)
{
Console.WriteLine(e);
}
Console.ReadLine();
で結果は?
2
3
4
5
おお!できてる。わしMap 実装しちゃったよ!俺カッケー度が高いテクニックですね。flatMap も同じように実装できるかも! 拡張メソッド理解できるとカッコいいですね!