はじめに
記事の投稿数も 20 を超えたということで、ポエムでも書いてみようと思います。
コードを書いていて、自分がよく知らないクラスを使うときにとりあえず .
を入力してクラスのメンバの一覧を表示することがあります。このとき候補にたくさんのメンバーが表示されると少しだけうんざりした気持ちになります。
要はひとつのクラスに詰め込みすぎるとひとつひとつの機能が見えにくくなりますよ、というお話です。
今回はあくまで筆者が日頃そう感じる、程度の内容です。フレームワークや言語設計に注文をつけているわけではありません。
具体例
上の例では単一責務の原則 https://ja.wikipedia.org/wiki/%E5%8D%98%E4%B8%80%E8%B2%AC%E4%BB%BB%E3%81%AE%E5%8E%9F%E5%89%87 に倣えば実際は起こりにくいです。
一方でやむなく巨大クラスになる場合はしばしば起こり得ます。System.Windows.Forms.Control のような、深く継承を使った例では小さなクラスにまとめるのは難しいでしょう。
今回はより身近な例として、System.Collections.Generic.IEnumerable<T> の拡張メソッド群について取り上げてみます。
IEnumerable<T>
拡張メソッド群
拡張メソッドは C# 3.0 に導入されました https://ufcpp.net/study/csharp/sp3_extension.html。同じタイミングで LINQ https://ufcpp.net/study/csharp/sp3_linq.html も導入されました。LINQ 機能を実現する上で拡張メソッドが有用だった、という背景です。
C# 13.0 /.net9.0 時点でこの IEnumerable<T>
には 79 個もの拡張メソッドが定義されています(オーバーロードを含めると 199 個)。.
を入力してクラスのメンバーの一覧を出したらスクロールバーが小さすぎて笑えたのは誰しもが通る道でしょう。
自分はそのクラスにどんなメンバーがあるか、上から候補を確認する舐める作業をよくやるのですが、メンバーの数が多いとこの作業をやる気が起きなくなります。拡張メソッドによってメンバーが埋もれている場合も同様です。
また、拡張メソッドのオーバーロードが大量にある場合も、パッと見どれが適切なのかわかりにくい問題があります。オーバーロードなので基本的にはどれが呼ばれても問題はないのですが、オプションを渡して機能を調整する系のオーバーロードは見落としたくないところです。クラス本体のメソッドシグネチャとバッティングした場合クラス本体のメソッドが優先されるわけですが、コードを見ただけでは分かりづらくエディタの該当メソッドにマウスカーソルを当ててどのメソッドか確認する作業を強いられます。オーバーロードに関してはジェネリック、オプション引数、可変長引数、特殊なパターンなどで複雑になりがちです。
IEnumerable<T>
拡張メソッド群の本体は System.Linq.Enumerable
で、ここに大量の拡張メソッドが定義されています。IEnumerable<T>
とは別に定義されているので、候補がノイズに感じるときは非表示にする方法はあります。
拡張メソッド本体のクラスを、現在のコードから見えないように名前空間を未参照にします。
// using System.Linq;
ただ、最近は C# プロジェクト単位で global using
していることもあるので、この方法は使えないこともあります。
<!-- <ImplicitUsings>enable</ImplicitUsings> -->
拡張メソッドに関するあれこれ
- 継承できない型(
sealed
、値型、列挙型)にメソッドを付け足せるので便利な機能 -
public
にすると影響が大きいのでinternal
にして気軽に使う - インターフェイスに関してはデフォルト実装 https://ufcpp.net/study/csharp/cheatsheet/ap_ver8/#default-imeplementation-of-interface という選択肢もある
- 本来はインターフェイスの型を使うユーティリティ型のメソッドにするべきだが、そうすると存在を忘れるため、目立たせるために拡張メソッドにする使い方を稀にする
おわりに
個人的にはクラスは小さいほうが扱いやすくて好みで、メンバーは少ないほうがクラスを把握しやすいです。
今回は IEnumerable<T>
の例を出しましたが、これは歴史的な理由もあってこうなっているので仕方のないところでもあります。
突き詰めるとアセンブリは小さいほうがいい、アプリは小さいほうがいいみたいな話になりそうですが、それは別の機会にでも。
自分はマネージャクラスにあれこれ詰め込んで肥大化させる癖があるので、適切にリファクタしてクラスを小さく保ったほうがいい、という自戒を含めたお話でした。