拡張メソッド
ここのところtraitに惑わされてだいぶ寄り道して凝った内容を書いてしまったのですが、個人的にはミックスイン(mixin)的に型階層に関係なくスコープを限定して後付けで機能を追加できればそれで充分に嬉しいと思っています。
C#の拡張メソッドはまさにそのような目的のためにあります。
具体的に実用的でRubyなどでありがちな
class Program { static void Main(string[] args) { 3.Times(() => Debug.WriteLine("Hello!")); Debug.WriteLine("-----"); 3.For(i => Debug.WriteLine("Hello! for " + i.ToString())); Debug.WriteLine("-----"); 9.For(2, i => Debug.WriteLine("Hello! for " + i.ToString())); Debug.WriteLine("-----"); string[] strArray = { "you!", "me!", "the world!" }; strArray.ForEach(str => Debug.WriteLine("Hello! for " + str)); Debug.WriteLine("-----"); int[] intArray = { 1, 3, 5 }; intArray.ForEach(i => Debug.WriteLine("Hello! for " + i.ToString())); Debug.WriteLine("-----"); double[] doubleArray = { 1.1, 3.3, 5.5 }; doubleArray.ForEach(d => Debug.WriteLine("Hello! for " + d.ToString())); } }
この様なソースで
Hello!
Hello!
Hello!
- -
Hello! for 1
Hello! for 2
Hello! for 3
- -
Hello! for 1
Hello! for 3
Hello! for 5
Hello! for 7
Hello! for 9
- -
Hello! for you!
Hello! for me!
Hello! for the world!
- -
Hello! for 1
Hello! for 3
Hello! for 5
- -
Hello! for 1.1
Hello! for 3.3
Hello! for 5.5
こんな結果が得られる拡張メソッドの使用例を紹介しておきます。
public static class IntExtention { public static void Times(this int number, Action action) { for (int i = 0; i < number; i++) { action(); } } public static void For(this int number, Action<int> action) { for (int i = 1; i <= number; i++) { action(i); } } public static void For(this int number, int step, Action<int> action) { for (int i = 1; i <= number; i += step) { action(i); } } } public static class ArrayExtention { public static void ForEach<T>(this T[] array, Action<T> action) { foreach (T item in array) { action(item); } } }
拡張メソッドはグローバルに適用されるのではなく、該当する拡張メソッドを含む名前空間をusingしたファイル内でだけ適用されます。
これによって拡張を利用するコンテキストを限定することができます。
先祖のクラスに適用される拡張メソッドは子孫のクラスでも利用することができます。
繰り返しになりますが、拡張メソッドではあくまでも機能を追加するためにメソッドを追加するのみで、多重継承やtraitの様にメソッド追加だけでなく既存のメソッドまでも型階層に組み込んでoverrideにより振る舞いを変えるような機能の拡張は意図していないと思います。
C#ではクラス単位の継承に頼らずにdelegateやラムダ式を利用してメソッド単位の委譲を使ってこの手の問題に対処することを指向している様に思います。
このようなアプローチはスマートではなくてソースコード記述上で冗長になるかもしれませんが実現したいことに対してより直接的な表現で一般的にはわかりやすいのではないかと思っています。