議論を警戒する必要があるのはいつnull
ですか? 理想的には、私はnull
どこでもガードしますが、それは非常に肥大化して退屈になります. また、人々は s のようなものにガードを付けていないことにも注意してくださいAsyncCallback
。
多くの単一言語コードで他の人を困らせないようにするために、どこを守るべきかについて受け入れられた標準はありますnull
か?
ありがとう。
議論を警戒する必要があるのはいつnull
ですか? 理想的には、私はnull
どこでもガードしますが、それは非常に肥大化して退屈になります. また、人々は s のようなものにガードを付けていないことにも注意してくださいAsyncCallback
。
多くの単一言語コードで他の人を困らせないようにするために、どこを守るべきかについて受け入れられた標準はありますnull
か?
ありがとう。
私がよく使ってきたアプローチの 1 つは、null オブジェクト パターンです。 たとえば、引数に基づいてインターフェイスのさまざまな実装を返すファクトリ クラスがあり、指定された引数がどの実装にもマップされていない場合、NullObject を返します。
public interface IFoo{
void Bar();
}
public class NullFoo{
public void Bar(){
//null behaviour
}
}
public class FooFactory{
public IFoo CreateFoo(int i){
switch(i){
case 1:
return new OneFoo();
break;
case 2:
return new TwoFoo();
break;
default:
return new NullFoo();
break;
}
}
}
IFoo
fromを取得したい場合CreateFoo
、返されたオブジェクトが null かどうかを確認する必要はありません。
明らかに、これは多くのアプローチの 1 つにすぎません。null はさまざまなことを意味する可能性があるため、「万能」というものはありません。
null 引数を防ぐもう 1 つの方法は、CodeContract preconditionsを使用することです。例えば
public void Foo(Bar x){
Contract.Requires<ArgumentNullException>( x != null, "x" );
//access x
}
コード コントラクトを使用すると、コードに対して静的コード分析を実行し、Foo(null)
. (詳細はこちら)
それを行うもう1つの理由は、非常に単純な汎用拡張メソッドを使用することです。
public static class Ex
{
public static void EnsureNotNull<T>(this T t, string argName) where T:class
{
if(t == null)
{
throw new ArgumentNullException(argName);
}
}
}
次に、次のように引数を確認できます。
public void Foo(Bar x, Bar y){
x.EnsureNotNull("x");
y.EnsureNotNull("y");
}
null 引数に対してガードする必要があるのはいつですか?
パブリック メソッドまたは作成したコードのコンストラクターに渡される null 引数について話していると仮定します。また、null を返す可能性のある外部依存関係を呼び出すときはいつでも、null を "保護" する必要があるかもしれないことに注意してください。
null 値が有用で明示的な意味を持たないユーザーに公開するすべてのパブリックメソッド (コンストラクターとプロパティ セッターを含む) で null を防止する必要があります。null 値がコードにとって特別な意味を持たない場合 (たとえば、配列の終わり、「不明」など)、その値を受け入れるべきではなく、ArgumentNullException
代わりに をスローする必要があります。
この規則はどちらにも固有のものではありませんnull
。パブリック メソッドに渡される引数を常に確認する必要があります。
たとえば、ユーザー ID を受け取り、ユーザーに対して何かを行う (たとえば、ユーザーを削除する) Web サービス メソッドを作成しているとします。メソッドがそのユーザーに対して何かを行う前に、それが有効なユーザー ID であることを確認する必要があります。もう 1 つの例は、コレクションまたは配列へのインデックスを取る public メソッドを作成している場合です。インデックスが許容範囲内にあること、つまりインデックスがコレクションより大きくないこと、またはゼロ未満であることを事前に確認する必要があります。
また、人々は AsyncCallbacks のようなものにガードを付けていないことにも注意してください。
メソッドの事前条件がメソッドに渡される引数によって慎重に保持されていることがわかっている場合 (そうでない場合は例外をスローするため)、プライベート メソッドと内部メソッド、またはプライベート メソッドと内部メソッドでこれらのチェックを自由にスキップできます。クラス。
ただし、ご指摘のとおり、作成していない API からの戻り値や、コールバック メソッドに渡された値を信頼しないように注意する必要があります。それらを「ダーティ」として扱い、null または無効な値である可能性があると想定します。
それは非常に肥大化して退屈になります
パブリック メソッドの前提条件を指定して追跡することは、肥大化することではなく、コンパイルされたドキュメントです。これは、コードが正しいことを確認する方法です。これは、コードのユーザーが何か間違ったことをしたことを前もって知らせる方法です。これをメソッドの途中で (または漠然と関連するクラスの別のメソッドで) 失敗させると、問題のデバッグがはるかに難しくなります。
これは今では大したことではないように思えるかもしれません。しかし、スタックが 5 レベル下がって顧客から苦情を受け始めたらNullReferenceException
、後で 20 回のメソッド呼び出しを行うと、メリットが見え始めると思います :)
多くの単一言語コードで他の人を困らせないようにするために、null をどこで保護する必要があるかについて、受け入れられている標準はありますか?
通常、人々if ... throw
はメソッドの先頭にコードを書くだけです。これは最も慣用的な構文であり、初心者でも非常に理解しやすいものです。Lispの括弧のようなもので、一度そのパターンを使用すると、それについて考えることなく、非常に迅速にざっと目を通すことができます。
Visual Studio Code Snippetを使用すると、これらのチェックをより速く作成できます。
アサーション構文をサポートする共有コードを使用または構築することで、このコードを少し短縮できます。if ... throw
構文の代わりに、次のような行を記述しますAssert.NotNull(arg1, "arg1");
。インスピレーションが必要な場合は、NUnitフレームワークのアサーションと制約を参照してください。
Code Contracts APIも調べてみてください。これは、事前条件、事後条件、および不変条件 (これらの「ガード条件」の正式な名前です) をチェックするために設計されています。また、この検証の一部を実行時からコンパイル時に移動することもできるため、プログラムを実行する前に間違いを犯したことを見つけることができます。私は実際には見ていませんが、より簡潔な構文も得られるかもしれません。 編集:この API の一部を使用する短い例については、Pencho Ilchev の回答も参照してください。
パブリック API の null チェックは必須です。開発者は、null パラメーターの結果として他のあいまいな例外をデバッグしようとするよりも、null パラメーターが原因でエラーが発生したことをすぐに知っている方が簡単 (かつ安全) です。
内部的には、コンストラクターは同じ理由で null をチェックするのに適した場所です。クラスでメソッドを呼び出す場合よりも、コンストラクターでキャッチする方が簡単です。
あらゆる場所をチェックするのは良いことだと主張する人もいますが、実際にコードを読むときにふるいにかけるコードが増えると思う傾向があります。
いくつかの一般的なメソッドの前提条件、つまり引数Preconditions
をチェックするための静的メソッドを含むクラスを作成することがあります。たとえば、次のようなものです。null
public static <T> T checkNull(T arg) {
if (arg == null)
throw new IllegalArgumentException();
return arg;
}
これにより、コードが少しきれいになります。
をチェックする場所に関しては、有効な値でない場合はどこnull
でも行う必要があります。
編集
この質問は C# としてタグ付けされていることに気付きましたが、アイデアはわかります...
メソッドの引数について話している場合は、選択肢があります。引数をチェックして throwArgumentNullException
するか、チェックを無視して何か他のものをNullReferenceException
さらに下にスローすることができます。引数をチェックしない場合のリスクは、NullReferenceException
がスローされる前にコードが何らかのグローバル状態を変更し、プログラムが不明な状態のままになる可能性があることです。
一般に、メソッドがスローする引数とドキュメントを確認することをお勧めしますArgumentNullException
。はい、もう少し手間がかかりますし、メソッドの開始時にこれらのチェックをくぐり抜けるのは面倒です。引き換えに得られるより堅牢なコードが適切なトレードオフであるかどうかを自問する必要があります。
この問題に関する適切な議論については、このリンクを参照してください。