未実装を活用する方法と機能を削除する方法【C#】

本来、プログラミングはコードで機能を実装していくものですね。

しかし、プログラミング言語には未実装であること、機能を廃止するための仕組みを持ちます。
C#も未実装と廃止の仕組みを持っています。

なぜ、未実装と廃止が必要なのでしょうか。

そこで今回は、C# における
「未実装がある理由」「コードを未実装する方法」「廃止がある理由」「コードを廃止する方法」
について紹介します。

未実装の使いどころ

未実装をつかうと開発の方法に幅を持たせることができます。
未実装をうまく使えば、ある程度柔軟に開発できるかもしれません。

テスト駆動での利用

未実装はその名の通り実装前、つまりコードを作る過程で利用されます。

例として、開発手法のひとつ、テスト駆動開発から見ていきましょう。

この開発手法をざっくり説明しますと

プログラムの実装より前にテストコードを書き、
テストコードに合格するように実装とリファクタリングを進めていく方法

です。

テストコードを先に記述する関係上、機能本体はその間は未実装のままです。

コードの設計と実装を分けられる

また、チーム構成でコードをリードできるプログラマと記述要員のプログラマに分かれているときも未実装が使えます。

メソッドの役割がしっかり把握しているプログラマが先にメソッドをバンバン未実装で定義していき、

あとは別のコーダーが実装するというチーム構成のときに未実装と相性がよいです。

実装したいところから実装するのに使う

逆に、コードを書くための設計が十分でないときも活躍します。

すべての開発でコードとイコールになるまで詳細な設計書を作るわけではありません。

書きながら、「この機能必要そうだ」とToDoリストの代わりに、メソッドを未実装定義していくときもできます。

いずれも未実装をうまく使えれば、作った場所からうごかせたり、レビューできたりと柔軟に開発できます。

未実装の書き方

未実装を表現するのは難しくありません。


メソッドが呼ばれたときに、未実装の例外を発生させます。

未実装のときはNotImplementedExceptionを使おう

実装し忘れていることに気づくためにはNotImplementedExceptionの利用が欠かせません。

メソッドは定義したなら実装しきるか未実装にするかです。

もしNotImplementedExceptionを記述できれば、思わぬバグを減らせます。

コンパイルエラー回避のためにreturn 〇〇はNG

コンパイルエラー回避のためにreturn 〇〇は意図せずバグを生み出す可能性があります。

未実装のメソッドを試しに作ってみましょう。

たとえば、単純にintを返却するメソッドを未実装してみます。

側だけ用意すれば、当然コンパイルエラーが発生します。

未実装のままコンパイルエラーを回避しようとすると、ダミーデータを返却しなければなりません。

確かに、これでコンパイルエラーは回避できました。

しかし、その代償にこのメソッドが実行したときにエラーを吐かない、「実装されたメソッド」になってしまいました

もし、忘れずにのちのち修正できれば、よいのですけれども、

修正を忘れてしまうと実行時にエラーを発生させないので、修正忘れに気づきません。

これが、retun〇〇で未実装を避ける理由になります。

ここでしっかりと

throw new NotImplementedException();

と実行時に例外を発生させていれば、未実装に気づくことができます。

voidでもNotImplementedExceptionを使おう

「return○○を避ける」について説明しました。
しかし、最初から返値をもたない場合もあります。

そう、voidのときです。

voidの場合、定義した時からエラーがでないので、より未実装に注意したいですね。

throw new NotImplementedException();

で、しっかりスローしましょう。

未実装のままリリースしないために

未実装で柔軟にコード記述ができるようになりましたが

「未実装のままリリース」なんてことにならないように注意したいですね。

Gitのプルリクを活用

チーム開発をしている場合、特にコードレビューが入るのであれば、未実装でリリースなんてことは、まずないでしょう。

ただし、レビューアーがいるから安心と未実装ままレビューに提出すると、
「未実装でプルリクしないように」と注意がはいります。ちょっとばつが悪いですね。

会社なら評価に影響してしまうかもしれません。

未実装忘れ対策

そこで個人でできる対策です。
泥臭いでしょうが、未実装のままかどうかはグレップで確認するのが手っ取り早いと考えています。

visual studioでctrl + shift + fでファイル横断検索で
NotImplementedExceptionを検索すれば一発です。

結果が一覧で表示されますし、クリックすれば該当行に飛んでくれるので、
未実装箇所が簡単にわかります。

NotImplementedExceptionをコンパイルエラーにする方法は?

NotImplementedExceptionが実装忘れにつながるのであれば、
NotImplementedExceptionをコンパイルエラーにすれば良いじゃないか

とい意見があるかもしれません。

しかし、残念ながら NotImplementedExceptionをコンパイルエラーにする方法はありません。

未実装の使いどころでもふれましたが、

NotImplementedExceptionをコンパイルエラーにしてしまうと、
テストコードもコンパイルエラーになってしまい、

テスト駆動開発などもできなくなってしまいます。

throw new NotImplementedException();

を使う場合は、コンパイルエラーにせずに未実装にすることが目的になります。

メソッドやプロパティを削除するための方法【Obsolete】

プログラムを改善(リファクタリング)をするとき
メソッドやプロパティを削除することがあります。

C#にはコードを削除する仕組みとして、廃止属性Obsoleteが用意されています。

そこでObsoleteを使って、メソッドやプロパティを削除するための方法、
廃止予定から廃止までの流れをみていきましょう。

Obsolete の使い方

基本的な使い方は公式ドキュメントに記載があります。

Obsoleteの役割は2つです。

  • 代替案の提示
  • コンパイルエラーにするかどうか

属性で定義されていますので、このように使います。

Obsolete(“代替案の記載”,コンパイルエラーにするか)

です。

第一引数に、廃止する機能の代替案がわかるように文字列で説明しましょう。

第二引数のbool値は、利用した時にコンパイルエラーにするかどうかです。

今回の例ではfalseなのでコンパイルエラーにはしていません。

廃止予定ならコンパイルエラーにしない

廃止予定はあくまで警告です。

廃止予定のメソッドを使用していても、コンパイルエラーにはなりません。

ですが、いつかは無くなる機能なので、visual studio上に、しっかり警告を出してくれます。

警告 CS0618 ‘NotImplementService.ObsoleteVoidMethod()’ は旧形式です (‘こちらの機能は廃止予定です。代わりにVoidMethodを使用ください。’)

Obsoleteが付与されたメソッドの利用者は代替案に従い、
廃止までの間に別のメソッドに切り替えることができます。

廃止にするときはコンパイルエラーに

「もうこのメソッドは使わせません!」
「サポートしないぞ」

というときは、コンパイルエラーにしましょう。

第二引数をtrueに変えるとコンパイルエラーにできます。

この時点で、メッセージに廃止と明示してしまうのも良いと思います。

また、trueに変えることでvisual studio上でも表記が変わります。

赤の波線になって、

エラー CS0619 ‘NotImplementService.ObsoleteVoidMethod()’ は旧形式です (‘こちらの機能は廃止です。代わりにVoidMethodを使用ください。’)

エラーコードがCS0619でコンパイルエラーになりました。

メソッドの利用者はもう代替案を使うしかありませんので、
強制的にメソッドを廃止することができます

Obsoleteで廃止する理由

一度に全部修正できるのがベストですね。

なんで廃止(予定)があるのでしょうか。

ひとつには、コード修正の影響範囲が考えられます。

影響範囲を限定したいとき

まずは自分たちの組織だけで廃止を使う場合を例にとります。

改善したいメソッドが様々な場所で利用されている場合、修正の影響範囲が大きくなります。

改善するためのリソースが十分にあれば、問題ありません。

しかし、ビジネスシーンでは人的リソース、時間リソースが足りないこともよくあります。

そこで、影響範囲を限定して、特定範囲だけ切り出しバージョンアップさせます。

こちらをご覧ください。

Method-Cを修正するのにSomeMethodの修正がともなうとき、

影響がのこりのMethod-A,B,Dに波及してしまいます。

ですが、

このようにメソッドを分離すれば、Method-A,B,Dに影響を与えずに

Method-Cを改善が可能です。

一方で、似た機能が重複するという問題が発生します。

ここでObsoleteの出番です。

改善前のもともとあったSomeMethodは使ってほしくないため廃止予定でマークします。

こうすることで、影響範囲を限定することができ、
機能が重複していたとしても古いメソッドが新たに使われることを防止します。

そして、修正しきれなかった範囲はリソースに余裕がでたとき、別の機会に対応することができます。

ライブラリとして公開している機能を削除したいとき

ライブラリとして公開している機能を削除したいときもObsoleteが使われます。

理由は影響範囲の限定と基本はいっしょです。

ライブラリを公開している場合などは、利用者は同じチームとは限りません。

NuGetに公開したならば、世界中の個人や組織に利用してもらえます。

そんな世界中の人に利用されている機能が、いきなり消えたならば何がおきるでしょうか。

多くの利用者が、消えた理由と代替案を探さなくてはなりません。

このように、消したい機能の利用者が混乱を防ぐために
廃止予定→廃止を使い、徐々にフェードアウトさせていきます。

利用者が多ければ多いほど、機能削除は重要ですね。

実例として、マイクロソフトも機能廃止を使って、利用者に移行を促しています。

https://docs.microsoft.com/ja-jp/dotnet/api/microsoft.aspnetcore.builder.connectionsappbuilderextensions.useconnections?view=aspnetcore-3.1

消すじゃだめなの?

削除するのに廃止予定、廃止と段階を踏むのは億劫に感じるかもしれません。

確かに個人開発や意識統一のできているチームだとObsoleteは不要な場面もあります。

影響範囲はいつも修正しきれる!というチームならば出番はないでしょう。

ただ、しっかりと管理されていれば問題ないのですけれども、
ずさんな管理をしていると、

ライブラリでメソッドが突然無くなる
→ サービス稼働中でもコンパイルエラー
→ 緊急対応

こうなると大混乱ですね。

このような事が起きている、もしくは避けたいのであれば、
Obsoleteを使うのは良い選択肢になります。

また、いきなり機能を消してしまうと利用者は代替案がわからなくなりますね。

機能は使ってもらって初めて価値のあるものです。

今まで、機能を使ってくれた方々をないがしろにしない、

廃止・廃止予定は、メソッドやクラスの利用者に寄り添う仕組みだと思います。

まとめ

いかがだったでしょうか。

以上が、C#で実装する前と、実装を廃止する仕組みの紹介になります。

それでは最後にまとめます。

  • メソッドを作るときは実装完了 or NotImplementedException
  • NotImplementedExceptionで実装漏れのリスクを低減
  • 機能をコードから完全削除するには、Obsoleteを使って廃止予定→廃止→削除がスムーズ
  • 修正の影響を限定したいときにObsoleteを活用

では。