あんまり管理もしてない今日この頃ですが皆様いか(略)。
今回は型引数をバインドするカリー化ができないかと言う話。
一応解説しておくと、カリー化とは要するに引数の一部を固定した関数を作ることです。こんなサンプルコードでご理解いただけるでしょうか。
staticint Add(int x,int y) {return x + y; }staticint Add5(int x) {return Add(x, 5); }publicstaticvoid Main() { Console.WriteLine(Add5(4));}
この場合、Add メソッドの引数 y を 5 に固定した Add5 メソッドを新たに作成しています。まあサンプルのためのサンプルなんで意味は無いですけどね。ちなみに関数作成はラムダ式など使って表現することが多いです。
staticvoid Main() { Func<int,int> addx = x => Add(x, 5); Console.WriteLine(addx(4));}' ちなみに VB だとラムダ式はこんな形になるそうな。PublicSharedSub Main()Dim addxAs Func(OfInteger,Integer) =Function(x) Add(x, 5) Console.WriteLine(addx(4))End Sub
さて本題ですが、まず何をやりたかったのかから説明しましょう。
幾つかのアセンブリから、特定の型の派生クラスの一覧を調べようと思ったのがきっかけでした。具体的に言うと WPF の MarkupExtension の派生クラスを。
取りあえずベタに書きます。
publicstaticvoid Main() {var types1 =typeof(Window).Assembly.GetTypes() .Where(type => type.IsSubclassOf(typeof(MarkupExtension)));var types2 =typeof(Rect).Assembly.GetTypes() .Where(type => type.IsSubclassOf(typeof(MarkupExtension)));var types3 =typeof(Duration).Assembly.GetTypes() .Where(type => type.IsSubclassOf(typeof(MarkupExtension)));// 以下、types を Concat したり表示したり}
露骨に記述が重複しているので、これはメソッドにするのが一般的でしょう。
publicstatic IEnumerable<Type> GetMarkupExtensions(Type type) {return type.Assembly.GetTypes() .Where(t => t.IsSubclassOf(typeof(MarkupExtension)));}publicstaticvoid Main() {var types1 = GetMarkupExtensions(typeof(Window));}
引数が Type 型なら、一々呼び出し元で typeof するより、型引数で表現して GetMarkupExtensions メソッド内でtypeof した方がすっきりする気がします。
publicstatic IEnumerable<Type> GetMarkupExtensions<T>() {return GetMarkupExtensions(typeof(T));}publicstaticvoid Main() {var types1 = GetMarkupExtensions<Window>();}
ここでこの GetMarkupExtensions を汎用化することを考えます。つまり、任意のアセンブリ(に属する型)から任意の型の派生クラスを列挙するメソッドです。
publicstatic IEnumerable<Type> GetSubclasses<TAnyInAssembly, TBase>() {returntypeof(TAnyInAssembly).Assembly.GetTypes() .Where(t => t.IsSubclassOf(typeof(TBase)));}publicstaticvoid Main() {var types1 = GetSubclasses<Window, MarkupExtension>();var types2 = GetSubclasses<Rect, MarkupExtension>();var types3 = GetSubclasses<Duration, MarkupExtension>();}
汎用化した結果、逆に Main の方で記述が増えてしまいました。どうせ型引数のうち MarkupExtension は同じなんですからこれを固定して型引数一つで済ませたい。こんな感じです。
publicstaticvoid Main() {var func = BindToGetSubclasses<MarkupExtension>();var types1 = func<Window>();var types2 = func<Rect>();var types3 = func<Duration>();}
BindToGetSubclasses は Func<IEnumerable<Type>> 相当のデリゲートインスタンスを返すことになりますが、ここで困ったことになります。デリゲートインスタンスは作成時点で既に型引数は解決済みでなければならないのです。インスタンスですから当然ですが。
てなわけでメソッドでは解決できないようです。ここはもう一つ大きい区切り、つまりクラスに目をやります。クラスにも型引数を使用できます。ならばこちらでバインドしてやればいいのではないか。
publicclass SubclassEnumerator<TBase> {public IEnumerable<Type> GetSubclasses<TAnyInAssembly>() {returntypeof(TAnyInAssembly).Assembly.GetTypes() .Where(t => t.IsSubclassOf(typeof(TBase))); }}publicstaticvoid Main() {var e =new SubclassEnumerator<MarkupExtension>();var types1 = e.GetSubclasses<Window>();var types2 = e.GetSubclasses<Rect>();var types3 = e.GetSubclasses<Duration>();}
これでほぼ BindToGetSubclasses と同じ記述になりました。
しかし一々クラスを用意しなければならないってのはちょっと面倒ですね。
ここ(hongliang.seesaa.net)で公開しているものについて、利用は自由に行って頂いて構いません。改変、再頒布もお好きになさって下さい。利用に対しこちらが何かを要求することはありません。
ただし、公開するものを使用、または参考したことによって何らかの損害等が生じた場合でも、私はいかなる責任も負いません。
あ、こんなのに使ったってコメントを頂ければ嬉しいです。
この広告は90日以上新しい記事の投稿がないブログに表示されております。