BT

InfoQ ホームページ ニュース C#の今後 - パラメータnullバリデーションの簡略化

C#の今後 - パラメータnullバリデーションの簡略化

ブックマーク

原文(投稿日:2020/01/16)へのリンク

プロポーザル #2145は一見すると、C# 8のNullable Reference型機能の論理的拡張のように思われる。基本的な考え方は、非nullパラメータを受け入れるメソッドにおいて、引数のnullチェックを明示的に行う必要をなくす、というものだ。しかしながら、これが大きな論争の的になっている。

本レポートではこのオプションとそのメリット、デメリットを説明することで、読者が自身の意見を持つための一助としたいと考えている。その前に、これがなぜC# 8でも重要であるのかを簡単に説明しよう。

現在のNullable Reference型機能は、極めて情報量の乏しいものになっている。nullを扱う際の一般的なミスを開発者に警告するが、コンパイル時のみだ。アプリケーション実行中は、このようなコンパイル時チェックはまったく機能しない。

しかも、リフレクションやダイナミックを使用する場合には、コンパイル時のチェックも行われないのだ。

特別な構文 — 感嘆符(bang)演算子

元々の提案は、引数nullチェックを行うことをコンパイラに指定するために感嘆符演算子を使用する、といものだ。

//typed code
void Insert(string value!) 
{
    ...
}

//compiled code
void Insert(string value) 
{
    if (value == null)
        throw new ArgumentNullException(nameof(value));
    ...
}

このオプションを支持する意見は、これが低侵襲性であるということだ。C#コンパイラに対する変更は軽微で、新たな構文には完全な後方互換性がある。

その一方で、次のような反対意見がある。

  • 新たな構文を使用可能なユースケースが極めて狭い。
  • コードを読む際に読み落とす可能性型が高い。
  • 忘れやすい。
  • パラメータが非nullであるという宣言は冗長的である。

もうひとつの問題は、"値!"に"nullチェックをしてほしい"という意味と、"nullでないことは分かっているのでチェックの必要はない、という意味の2つがあることだ。この最後の問題を解決するために、プロポーザルの変更版として、2重感嘆符(double-bang)演算子(string value!!)を使用する方法も提案されている。

新しい属性

新たな構文に代わるもうひとつの選択肢は、コンパイラが認識する新たな属性を使用する、というものだ。

void Insert([NotNull] string value)

コンパイルコードに影響する属性はC#ですでに導入済みなので、既存のパターンにもマッチする。もし宣言的パラメータバリデーションを導入するのであれば、このような形式になると思われる。

その一方で、次のような反対意見がある。

  • 検討中の他の案に比較して、非常に冗長である。
  • パラメータが非nullであるという宣言は冗長的である。

コンパイラフラグ

議論中のもうひとつの選択肢は、グローバルコンパイラフラグである。これを有効にすると、すべての非nullパラメータがチェックされる。

この案のメリットは、何も考えなくてよいことだ。有効にすればチェックが自動的に追加されるので、既存の知識を捨てたり、新たな構文を学んだりする必要がない。

これに対する反論でまず指摘されているのは、パフォーマンスに関する懸念である。しかし、この案の支持者は、パフォーマンスコストは取るに足らない、パブリックメソッドにのみオプションで適応されるものである、メソッド呼び出しでは結局nullチェックが行われるものだ、などと反論している。

また、スローしたい例外は開発者によって違うのではないか、という意見に対しては、ArgumentNullExceptionに類するものをスローするべきだ、あるいは、特別な処理が必要ならば、ファイル単位あるいはメソッド単位でこの機能を無効にするコンパイラディレクティブを用意すればよい、という反論がある。

最後の意見は、この変更の持つ意味の大きさに関するものだ。コードのセマンティクスを変更するコンパイルフラグは、この変更を含めても2つあるに過ぎない。"nullable"などのコンパイラフラグは実際にコードの動作を替えるものではなく、コンパイル時のみに機能するものである。

このルールの例外は"checked"コンパイラフラグで、整数値のオーバーフローに関する動作を変更するのだが、C#言語設計者の間では、コンパイラレベルでのフラグ設定を見なければコードの動作を特定できないという意味から、このフラグは失敗策だと考えられている。

反論ではこの事実を認めながらも、Nullable Reference機能を完成に近付ける上で、この変更は必要なステップなのだ、という立場を取る。NRTは完全なソリューションを意識したものではないので、後方互換性の立場から、ランタイムの動作に影響を与えるべきではない、という意見もある。

外部AOPとILウィービング

"ILウィービング(weaving)"とは、コンパイル完了後のアセンブリを修正する後処理ステップのことである。この手法はPostSharpや、中断されたCode Contractsプロジェクトといった、アスペクト指向プログラミングツールで使用されている。

この論争で取り上げられているツールはFody NullGuardだ。FodyMono.Cecilをベースにした、MITライセンスのILウィーブツールである。

ILウィービングに対する反論は、サードパーティツールが必要なこと、IDE内で動作する静的分析ツールと相性が悪いこと、Edit-and-Continueを無効にすること、ビルドが遅くなること、などだ。

内部AOPとマクロ

内部AOPやマクロシステムに類するものに関する議論もいくつかある。これは、C#の拡張を待つ代わりに、開発者による言語そのものの拡張を可能にするものだ。

現時点では、この意見は多くの賛同を集めてはいない。内部AOPあるいはマクロシステムは、ツールチェーン全体を大幅に変更することになる。さらには開発者が事実上、自身のC#の方言を作り出すことにより、言語のフラグメントを発生させる可能性があるのだ。

何もしない

最後の選択肢は、単に何もしない、というものだ。これを支持する最も強い意見は、これは単なる"Quality of Life"機能に過ぎず、開発者に新たなものを何も提供しない、というスタンスである。ボイラプレートの削減という意味での価値は確かにあるが、どの提案においてもマイナス面がメリットを上回っている。

しかも、関数内でのnullチェックの必要なコードはごく一部に過ぎない。

この意見に対する反論は、nullチェックはC#のボイラプレートコードとしては最も一般的なもののひとつであり、コード例と実運用コードのいずれでもミスが多い、というものだ。これに対して、nullチェックのミスは、すべてではなくとも大部分が静的解析ツールで検出可能であり、NRTが有効ならばその精度はさらに高くなる、という意見がある。

この記事に星をつける

おすすめ度
スタイル

こんにちは

コメントするには InfoQアカウントの登録 または が必要です。InfoQ に登録するとさまざまなことができます。

アカウント登録をしてInfoQをお楽しみください。

HTML: a,b,br,blockquote,i,li,pre,u,ul,p

コミュニティコメント

HTML: a,b,br,blockquote,i,li,pre,u,ul,p

HTML: a,b,br,blockquote,i,li,pre,u,ul,p

BT

あなたのプロファイルは最新ですか?プロフィールを確認してアップデートしてください。

Eメールを変更すると確認のメールが配信されます。

会社名:
役職:
組織規模:
国:
都道府県:
新しいメールアドレスに確認用のメールを送信します。このポップアップ画面は自動的に閉じられます。