2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

using static禁止令

Last updated at Posted at 2023-02-17

C#のusingによるインポートの際に、using static 静的クラス名;とすると、クラスの静的メンバを読み込める。

using static System.Math;

public static void Main()
{
    var sine = Sin(0.5 * PI); // using staticが無ければ「Math.Sin(0.5 * Math.PI)」とする必要がある
}

こうすると、特に長いクラス名をいちいち連呼せずに済むのでコーディングが楽になる。しかし、これは「後は野となれ山となれ」である。

Javaの開発者は何と言っているか

静的インポートを先に実装したJavaではこんなことを言っている。

静的インポートはいつ使うの? めったに使わないでね! そうしないと定数のコピーを宣言したくなる場合や、継承を濫用(定数インターフェイスのアンチパターン)したくなる場合だけ使いましょう。言い換えるなら、2つ程度のクラスの静的メンバに頻繁にアクセスする必要がある場合です。静的インポートを使いすぎると、インポートした静的メンバで名前空間が汚染され、プログラムが読めなくなりメンテナンス不能に陥ります。コードを読む人(数か月後のあなたも含みます)には静的メンバがどのクラスにあるのか分かりません。クラスの全ての静的メンバを読み込むのは可読性を特に損ねます。メンバが1,2個だけ必要なら、別々にインポートしてください。正しく使えば、クラス名を繰り返す定型句が無くなるので、静的インポートはコードを読みやすくします。

――Java Documentationより英訳。強調は原文に則る

要するに可能な限り避けてほしいということだ。もはや筆者の言いたいことは出尽くしたように思うが、書いてないことを補足してみよう。

コピペができない

今回はMainのみの極小規模なコードだったが、いろいろなクラスがあるコードを考えてほしい。作っていくうちに同じ静的メンバが必要になってくることは多い。その際に大抵の場合はコードをコピペするだろう。するとコードはエラーを返す。上の例におけるSin()は大抵の場合コピペ先のクラスに存在しないメンバだからだ。

そこでコード上部のusing staticを見つけた。おめでとう、複数のusing staticがあって、どちらか分からないね! それを見極めるためにはIntellisenseで当該メンバの所属クラスを探さなければならない。インポートが複数クラスにまたがっていたら面倒だ。

C言語的発想

しかし、上記の問題はC# 10で導入されたglobal using staticで解決できる。これなら静的メンバの場所が分からなくても問題なさそうだし、修正ならVisual Studioの「実装に移動」を使えば問題無くできる。しかし、問題はそこではない。

そもそも、どの名前空間か分からない関数や変数というのが、C言語のグローバルメンバと同じような発想だ。オブジェクト指向言語のメソッドやフィールドはどこかに属していなければならない。C#ですべてのクラスがObjectを引き継ぐことからも分かるだろう。これを濫用するのはオブジェクト指向がまだ身にしみついていないということであろう。

改善方法

オブジェクトとして表現する

そもそも論として、オブジェクト指向ならなるべくクラス(動的なインスタンス)として表すべきだ。ポップアップメッセージならメッセージをクラスとして表現できないか模索する。これなら変数定義を巡ってあっちこっちたらいまわしにされるよりも、値のべた書きの方が意味合いが分かりやすくなる。Web系のプロジェクトならメッセージに付随させてHTMLクラス名などを盛り込んだりできる。

クラス化することで、それに付随する処理もパッケージングできるという利点がある。条件分岐が複雑になっているなら、なおさらクラス作成のモチベーションにつながる。

定数クラスはネ申オブジェクトである

さらに言えば、NewsImportConstantsというのはNewsImportManager同様曖昧模糊で、筋の悪い名前である。クラス化ができなかったり、使いまわす必然性があり定数にするのなら定数の種別ごとに静的クラスを切り分けるべきだ。例えばファイル拡張子を格納したいならFileExtensionsというクラスにするのが自然だろう。このように意味のある名前を使うという手間を踏むことで、自分が何をしたいかが分かりやすくなるし、読者にも当然伝わるようになる。そして、本来用途の違う定数を、たまたま同じクラスに入っていたから読んでしまうという失敗もせずに済むはずだ。

定数クラスの名前を的確かつ簡潔なものにする

例えば、クラス名がSupercalifragilisticexpialidociousとか言うなら静的インポートをしたくなるだろう。しかし、そのような場合はほぼ間違いなくクラス名に問題がある。FileExtensionsだったら書くのは苦でない。名前は分かりやすくあるべきだ。

まとめ

C#ではusing staticを使うべき理由はほとんど無いと思われる。C#にはインターフェイスに定数を定義できない(そもそもなんでJavaはインターフェイスに定数があるの?)ので、定数をコピーしたい場合しか必要無いからだ。おそらく、JavaでできることだからC#に導入しようという考えで実装された機能だろう。

using staticを使うならグローバルユージングしかないと思うが、それにより定数クラスをひとまとめにしたくなる欲が出てくると思われる。グローバルユージングが読み出しをひとまとめにできる機能だからだ。そうすると定数クラスのネ申オブジェクト化が助長される。using staticは百害あって一利も無い誰得機能としか言いようが無い。

この機能を使いたくなるのは大抵の場合コードから異臭がしている時だ。クラスと定数の関係を見直すいい機会ととらえて、クラスの名前を変更すべきだ。「名は体を表す」とはよく言われる言葉ではあるが、オブジェクト指向においてはまさにそうなのだ。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?