Obsolete属性は、型やメンバに付与する属性で、ある機能・API(型やメンバ)が廃止予定・非推奨である旨を、コンパイラの警告またはエラーによって通知するためのものです。 Obsolete属性を使うことにより、使用しようとしているAPIが何らかの理由で廃止された、あるいは非推奨となった(obsolete, 時代遅れ、廃れた)ことをコンパイル時に知ることができます。 Obsolete属性は、Javaにおける@Deprecated
アノテーションなどに相当する機能と言えます。
Obsolete属性により、APIの提供者はobsoleteであることを属性としてコード中で記述することができ、一方APIの利用者はobsoleteであることをコンパイル時に警告またはエラーとして知ることができます。 このため、obsoleteであることを示すドキュメント等の整備/参照によらずとも、それをコードのみで通知/把握することができます。
Obsolete属性では単に廃止予定・非推奨の通知をするだけでなく、警告・エラーとして表示されるメッセージも指定できるため、これにより代替機能やより洗練された実装への移行を利用者に対して促すことができます。
ここではObsolete属性についてと、Obsolete属性を使った具体例、機能の廃止・非推奨化について解説します。
Obsolete属性
Obsolete属性が付与された型・メンバを使用しようとすると、コンパイル時に警告またはエラーとして通知されるようになります。
ここでは、Obsolete属性の用途と使用例、Obsolete属性で通知できる情報等について解説します。
Obsolete属性の用途と効果
クラスライブラリの利用者側
.NETのクラスライブラリやサードパーティ製クラスライブラリの利用者側では、Obsolete属性の存在によってライブラリの型やメンバが廃止予定あるいは非推奨であることを知ることができます。 Obsolete属性が付与されたクラス・メソッドを呼びだそうとしている場合、コンパイラは自動的にObsolete属性の存在を検知し、警告あるいはエラーとして利用者に通知します。
例えば、ファイル名に使用できない文字のリストを取得しようとしてPath.InvalidPathCharsフィールドを参照した場合、コンパイラは次のような警告を通知します。
このように、利用者はコンパイル時の警告によって機能が廃止予定・非推奨であることを知ることができます。 このとき、警告として通知されるメッセージに代替機能についての情報が含まれていれば、それも得ることができます。
Obsolete属性による警告がない場合、別途ドキュメントを参照するなどしない限り機能が廃止予定・非推奨となっているかどうかを把握することはできませんが、Obsolete属性が付与されていれば警告あるいはエラーとしてそれを知ることができます。
先の例における警告メッセージでは、Path.InvalidPathCharsの代替としてPath.GetInvalidFileNameCharsメソッドが提示されています。 利用者は、これに従うことで非推奨となっている機能を使用しないコードに書き換えることができます。
このように、廃止予定・非推奨となっていることを知らずに利用しようとしていた場合でも、Obsolete属性によってそれをコンパイル時までに知ることができ、推奨される代替機能へ移行することができます。
GetInvalidFileNameCharsメソッド、ファイル名・パスに使用できない文字に関してはパスの操作 §.パスやファイル名に使用できない文字の取得 (GetInvalidPathChars/GetInvalidFileNameChars)で解説しています。
クラスライブラリの提供者側
クラスライブラリの提供者側では、バージョンアップに際して既存の機能を廃止したり、非推奨としたい場合、より洗練された実装への移行を促したい場合にObsolete属性を用いることができます。
既存の型やメンバに対してObsolete属性を付与することにより、それが廃止予定・非推奨であることを利用者側に通知することができます。 このライブラリを利用する側では、Obsolete属性が付与された型やメンバを使用しようとした場合、コンパイラが警告(またはエラー)を発します。
Obsolete属性では、引数messageで警告として出力するメッセージ(文字列)を指定することもできるため、単なる廃止予定の通知だけでなく、廃止予定とする経緯や理由、代替となる機能など、Obsolete属性の付与に関する具体的な説明も同時に通知することができます。
また、Obsolete属性の引数errorにtrue
を指定すれば、Obsoleteとなったメンバの使用を警告ではなくエラーとして通知させるようにすることができます。
利用者側では、このような型やメンバを使用しようとするとコンパイルエラーとなり、通常の手段では一切使用することができなくなります。 機能の利用には問題があり、機能へのアクセスを一切禁止したいような場合には、Obsolete属性によってコンパイルエラーとすることができます。
公開せざるを得ない機能の利用に対する警告としての使用
このほかにも、ライブラリ実装の都合あるいは実装の経緯上・互換性維持上の理由など、ライブラリ外に公開せざるを得ない(public
な)型やメンバが存在する場合にもObsolete属性を用いることができます。
例えば、(Path.InvalidPathCharsのように)過去のバージョンで公開していた機能を安全な形に隠蔽するようにした場合や、デザイナや外部ツール、テストコードなどから参照されることが主目的の、利用者の直接使用を意図しない機能などが考えられますが、ドキュメント等で記載されない型やメンバであっても、それがpublic
であれば入力候補等で表示される場合があります。
このようなメンバにObsolete属性を付与しておくことにより、利用者が誤って使用してしまうことや、仮に意図的に使用した場合でもその結果は保証できないことなどを通知することができます。
.NETのクラスライブラリにおいてもこのようなメンバが存在します。 例えばWebClient.AllowReadStreamBufferingプロパティなどはObsolete属性が付与されていて、ユーザーが直接使用することを想定としたものではないことが通知されるようになっています。
Obsolete属性によってこういったメンバーに対する呼び出しをエラーとなるようにし、直接呼び出せないようにした場合でも、リフレクションによって呼び出すことができます。
メンバの存在をデバッガやデザイナから見えなくする目的には、DebuggerHidden属性やDebuggerNonUserCode属性、EditorBrowsable属性を用いることができます。 DebuggerHiddenAttribute属性・DebuggerNonUserCode属性について詳しくはデバッグ操作と属性を参照してください、
簡易なコード解析としての使用
Obsolete属性本来の目的とは異なりますが、IDEによるコード解析機能が使えない場合に、メンバを参照している箇所をコンパイラに検出させる用途でも使うことができます。 例えば、次のようにメソッドにObsolete属性をつけることによって、そのメソッドを呼び出している箇所を警告として表示させることができます。
ユーザー定義の警告・エラーを表示したい場合は、#warningディレクティブや#errorディレクティブを用いることができます。
警告ID・エラーIDの割り当て (DiagnosticId)
.NET 5以降では、Obsolete属性にDiagnosticIdプロパティが追加されています。 このプロパティはObsolete属性に診断用の固有なIDを割り当てるもので、コンパイル時にはこのIDが警告・エラー番号としても表示されます。 言い換えると、独自のコンパイル警告・エラーのIDを定義して個々のObsolete属性に割り当てるものと見ることもできます。
通常、Obsolete属性によるコンパイル時警告はCS0618
(C#)/BC40000
(VB)、コンパイルエラーはCS0619
(C#)/BC30668
(VB)として通知されますが、DiagnosticIdプロパティによってIDが割り当てられている場合は、そのIDがコンパイル時に警告・エラーのIDとして表示されます。
このプロパティは、Obsolete属性が付与された理由や経緯、対象の機能による分類など、任意に定めたルールに基づくIDを割り当てる目的に使用することができます。
例えば「脆弱性の発見によるAPIの廃止」に該当するObsolete属性にはVULN01
、「より洗練されたAPIの追加に伴う非推奨化」に該当するObsolete属性にはDEPR01
のようにIDを割り当てたり、ある機能FのAPIに適用されるObsolete属性にはFEATURE-F_01
のIDを使用する、といったように、ルールを任意に定めてIDを割り当てることができます。 IDの形式は特に定められていないため、DiagnosticIdプロパティには任意の文字列を指定することができます。
.NETのクラスライブラリでは、.NET 5以降の変更で付与されたObsolete属性にIDSYSLIBxxxx
が割り当てられています。 SYSLIBxxxx
は付与された理由によって個別にIDが割り振られていて、その一覧は.NET 5 以降の古い機能 - .NET Core | Microsoft Docsにて参照することができます。 例として、「UTF-7エンコードは安全ではない」ことを理由に非推奨とされた型やメンバにはSYSLIB0001が割り当てられています。
また、DiagnosticIdプロパティでは個別の警告IDが割り当てられることになるため、コンパイルオプション-nowarn
/<NoWarn>
や#pragma warning disable/restore CSxxxx
(C#)や#Disable/Enable Warning BCxxxxx
(VB)といったディレクティブによる警告の抑止を、Obsolete属性による警告全てではなくIDに対して個別に行うことができるようになります。
このほか、UrlFormatプロパティと組み合わせて使用することにより、プレースホルダが設定されたURLにこのIDを展開し、理由や経緯を説明するドキュメントへのリンクとして提示することもできます。
URLの埋め込み (UrlFormat)
.NET 5以降では、Obsolete属性にUrlFormatプロパティが追加されています。 このプロパティは、IDE上やコード解析において何らかのドキュメントへのリンクとして使われることが想定されていて、Obsolete属性が付与された理由や経緯などの詳細や、具体的な回避策および代替手段を提示するドキュメントのURLを指定します。
名前がFormatとなっていることから示されるように、URLにはプレースホルダを含めることができ、DiagnosticIdプロパティの値を展開したURLを得ることができるようになっています。 例えばUrlFormatにhttps://example.net/api/doc-{0}.html
を設定すると、DiagnosticIdがDEPR01
ならURLhttps://example.net/api/doc-DEPR01.html
として展開されます。 このため、短縮URLやクエリ付きのURLなど、特定の形式に従ったURLを設定することができます。
ただし、DiagnosticIdプロパティとは異なり、(少なくとも.NET 5時点の)dotnet build
によるコマンドラインのビルドや、(少なくともVersion 16.8.1時点の)Visual Studioでは、UrlFormatプロパティに指定されたURLは警告・エラーメッセージ内には含まれないようです。
Roslyn(Microsoft.CodeAnalysis)によるコード解析では、DiagnosticDescriptor.HelpLinkUriプロパティよりDiagnosticIdが展開されたURLを取得することができます。 このため、将来的にはIDEやコマンドラインでも展開されたUrlFormatが表示されるようになる可能性が考えられます。
入力ソースtest.cs
として先の例のコードを使用した場合。
入力ソースtest.vb
として先の例のコードを使用した場合。
Obsolete属性を適用できる個所
Obsolete属性は、次のようにクラス・構造体・インターフェイスなどのすべての型、およびメソッド・プロパティ(アクセサ含む)・イベント・フィールドなどのメンバに対して適用することができます。 また、public
ではない型・メンバにも適用することができます。 アセンブリ全体をObsoleteにすることはできません。
Obsolete属性が付与された型のメンバ
Obsoleteな型のObsoleteではない静的メンバ(static
/Shared
)の参照では、型の参照がObsoleteとなるため、静的メンバが実際にObsoleteであるか否かに関わらず、常にObsoleteであるかのような状態になります。 一方、Obsoleteな型のObsoleteでないメンバでは、インスタンスを介した参照はObsoleteとはなりません。
リフレクションによるObsoleteなメンバの呼び出し
仮に型やメンバにObsolete属性が指定されている場合でも、リフレクションによって呼び出す場合は警告やエラーは一切出力されません。 次の例ではエラーとなるObsolete属性を指定したメソッドを用意し、それをリフレクションによって呼び出しています。
上記のように、エラーとなるObsolete属性が指定されているメソッドでも、リフレクションを使って呼び出す場合はコンパイルエラーにはなりません。 また、呼び出し時にメンバがObsoleteであることを理由とした例外がスローされることもありません。
これを言い換えると、リフレクションによってメソッド呼び出し等を行っている箇所は、仮にそのメソッドがObsoleteになっている・なった場合でもコンパイル時には検知することができないということになります。 逆に、Obsolete属性によって直接の呼び出しがコンパイルエラーとなる場合でも、リフレクションによってそれを回避して呼び出すことができるとも言えます。
いずれにしても、リフレクションを用いる場合はこのようなリスクとメリットを考慮した上で行う必要があります。
Obsolete属性とXMLドキュメントコメント
C#やVB.NETでは、XMLドキュメントコメントによってメソッドの機能・動作・引数と戻り値の説明などを記述することができます。 しかし、Javadocにおける@deprecated
タグのような、廃止予定・非推奨となった旨を記述する要素は定義されていません。 具体的には<obsolete>
や<deprecated>
のような要素は存在しません。
そのため、廃止予定・非推奨に関する記述は<summary>
要素や<remarks>
要素として記述するか、UrlFormatプロパティのURLとしてリンクされる個別のドキュメントとして記述する必要があります。
なお、Javadoc コメントと等価な XML ドキュメントおよび<deprecated> (JavaScript)によれば、J#では<obsolete>
要素、JavaScriptでは<deprecated>
要素が定義されています。 C#やVB.NETでも独自の要素として<deprecated>
要素を使った記述をすること自体は行えますが、公式には定義されていない要素となるため、ビューアーやドキュメントジェネレータが対応していない限りは未定義要素として表示されるか、あるいは単に無視されることになります。
この他、XMLドキュメントコメントで用いることができる要素と意味についての詳細はXMLドキュメントコメントを用いたドキュメントの作成 §.XMLドキュメントコメントの要素を参照してください。
.NETクラスライブラリの非推奨な型・メンバ
.NETのクラスライブラリでは、BinaryFormatter・SoapFormatterといったクラスや、Uri.MakeRelativeメソッドやPath.InvalidPathCharsフィールドといったメンバなどにObsolete属性が適用され、非推奨となっています。
このほかにも、ドキュメントの以下のページにて非推奨となった型・メンバの一覧が掲載されています。
- .NET 5 以降の古い機能 - .NET Core | Microsoft Docs
-
破壊的変更の種類 - .NET Core | Microsoft Docs
- 互換性に影響する変更 - .NET Core | Microsoft Docs (.NET Frameworkから.NET Core、.NET Core 2.2から3.1など)
- .NET Framework で互換性のために残されている機能
公開されたAPIの廃止とObsolete属性
ここでは、クラスライブラリで一度提供を開始したメソッドをObsoleteとし、最終的に廃止するまでをリリース毎に考えていきます。 機能の廃止や置き換えに関するリリースポリシーの一例としてご覧ください。
バージョン1.0 (APIの提供開始)
まず、最初のリリースでは以下のようにメソッドを提供したとします。
バージョン2.0 (APIの非推奨化)
実装に問題がある、より洗練された実装を提供するなどの理由により、今後のリリースではこのメソッドの利用を推奨しないことになったとします。 これに従い、メソッドにObsolete属性を追加することによって利用者にその旨を通知します。 同時に、代替となる新しいメソッドも用意します。
このとき、旧式メソッドと動作が変わらない代替メソッドを提供できる場合や、動作が変わっても問題ないと判断する場合は、旧式メソッドから代替メソッドを呼び出すようにして機能を完全に置き換えてしまうこともできます。
バージョン3.0 (APIの廃止)
次のリリースでは、これまでに廃止予定としたメソッドを廃止することになったとします。 これに従いObsolete属性のメッセージを変更し、またこれまで警告としていたメソッドの利用を、error = true
を追加してエラーとして扱うように変更します。
この時点で、利用者は旧式のメソッドを使うことができなくなり、代替メソッドを使用しなければならなくなります。
Obsolete属性による警告・エラーは、コンパイル時にのみ発せられます。 従って、ライブラリを参照するコンパイル済みのコードがある場合、ライブラリ側でObsolete属性が警告からエラーに変わったとしても、コンパイル済みコードの実行には影響しません。 再コンパイルする時点で初めてエラーとして通知されます。
実行可能ファイルを再コンパイルせず、コンパイル済みのライブラリだけを置き換えてバージョンアップするようなシナリオの場合、Obsolete属性のerrorパラメータがfalse
からtrue
に変わっても、引き続き旧式のメソッドが呼び出されることになります。
旧式メソッドの廃止に伴い、実装も削除すると判断した場合は、次のように例外NotSupportedExceptionをスローするようにすることもできます。
errorパラメータをtrue
にした時点で、新たにコンパイルするコードではエラーとなり、利用できない状態になります。 しかし、コンパイル済みのコードからは依然として呼び出し自体は可能であるため、そういった呼び出しも禁止したい場合はこのように例外をスローするようにします。
実装されていない旨を強調するために、NotSupportedExceptionではなくNotImplementedExceptionをスローすることも考えられます。 ただ、NotImplementedExceptionでは今後実装される可能性のニュアンスを含むため、完全に廃止された機能の場合はどちらの例外がより妥当かどうかは判断が分かれます。
.NETでは、ObsoleteExceptionやDeprecatedExceptionといったようなこの状況に適した例外クラスは用意されていません。 十分な必要性があるなら、NotSupportedExceptionあるいはNotImplementedExceptionを継承してこのような例外クラスを作成し、スローするということも考えられます。
バージョン4.0 (APIの完全削除)
ここまでの時点では、機能自体は廃止したものの、クラスライブラリのAPI互換性は維持されています。
クラスライブラリのAPI互換性を破壊するようなリリースを行っても問題ないと判断する場合は、次のように完全にメソッド自体を削除した上でリリースすることもできます。
ここまでの例でライブラリのバージョンを明記するために用いた属性AssemblyVersionについてはアセンブリのバージョン情報を設定・取得するなどを参照してください。