複数のサイトでZoran Horvatの「C#コードをよりオブジェクト指向にする」を見ていましたペイウォール。そして彼は次のように言っています:
_if(obj != null) { obj.DoSomething(); }
_
そのタイプの項目の数が1または0のリストが必要です:
list.forEach(()=> obj.doSomething());
リストにオブジェクトがない場合、操作は実行されませんが、リストにオブジェクトがある場合、操作が実行されます。これにより、nullを超えて分岐する必要がなくなります。
しかし、私があなたの助けを必要としているのは、ここでnullでの分岐を回避する必要性を理解することですか? forEachはここと同じではありませんか?
どのようなメリットがありますか?
ForEachはここと同じではありませんか?
分岐がイエスになる限り。ループには分岐があります。
分岐を回避する必要がある場合(おそらく 分岐予測 最適化の問題が原因である可能性があります)、分岐なしの修正は nullオブジェクトパターン であり、DoSomething()
静かに何もしないメソッド。
それを行うと、コードは次のようになります。
obj.DoSomething();
ここで、何もする必要がない場合、obj
は、クワイエットnullオブジェクト、またはnullポインタ例外をスローする古典的なノイズの多いnullになります。どちらにしても分岐はありませんでした。分岐予測器が遠く離れているのを見た設定アドレスにジャンプするだけです。これは、ポリモーフィックオブジェクト指向ソリューションです。
だから何を使って
list.forEach(()=> obj.doSomething());
あげる?
それはそれを必要とするすべての型に対して特別なクラスを作成する必要なしに「静かなnull」を作成する方法を提供します。これは空のコレクションの形式をとります。空の文字列 ""の形式でこのバージョンをすでに使用している可能性があります。ここでは、リストを使用して同じことを行っています。
エンジンは常に改善されているので、分岐予測がそれをどの程度うまく処理するかは言いません。しかし真剣に、あなたがする必要がない限りそれについて心配しないでください。最初に読みやすさを心配し、後で来る人はそれを読むことができるので、必要なだけ速く読むことができます。
これらすべてが無視しているのは入力検証です。入力を信頼できない場合は、チェックする必要があります。 nullが入力可能であるが有効な入力ではない場合は、チェックする必要があります。これは、型システムがノイズの多いnullを受け入れないと言うことを許可しない場合に問題を混乱させます。
ノイズの多いヌルを除く理想的な世界では、コンパイル時に発生する可能性があります。残念ながら、C#の型システムが最初に作成されたときは 彼らが決定した すべての参照型はnull可能である必要があります。
ただし、C#8には、null不可の参照型という新しい機能があります。現在、Foo
はnullにできません。 Foo?
のみです。したがって、C#8ではnullの入力検証は必要ありません。型システムでは、ノイズの多いヌルを受け入れないと言えます。ノイズの多いヌルを取得する唯一の理由は、ヌルを受け入れることにしたためです。
幸いなことに、これらのnull不可の参照型は、ノイズの多いnullを除外するだけです。静かに何もする必要がない場合でも、静かなヌルは問題なく機能します。
あなたは2つの別々の問題に取り組んでいるようです。 1つ目は、リスト内のアイテムがnullかどうかをチェックすること、2つ目は、リストが空であるという特別なケースを処理することです。 2番目のスニペットは、nullリストの場合には機能せず、null参照例外をスローします。
コンピュータは高速ですが、プログラマは高速ではありません。ほとんどの場合、プログラマーが(nullチェックでより明確になるように)物事をより簡単にし、コンパイラーにそれをより効率的にする方法を考えさせる方が良いです。
重要なのは、null
を使用して特殊なケースを通知するべきではないということです。
具体的な状況に応じて異なるアプローチがあります。
メソッドは(リテラル)null
参照を返してはなりません(null
が呼び出し側で特別な処理を必要としない結果セットの有効な部分でない限り)。
null
を返す代わりに、通常exceptionをスローする方が適切なアプローチです。ただし、注意してください。制御フローを実装するために例外を使用しないでください。
null
を返す代わりに、メソッドの宣言された戻り値の型であるNull Equivalent Objectを返すこともできます。これはNull Equivalent Objectは通常constantです。
C#についてはあまり詳しくありませんが、JavaにはOptional
があり、null
。このオプションは、値がConsumer
でない場合にnull
に渡すため、呼び出し元自体がif(x!=null)
ブランチを実装する必要はありません。同様の機能があります。
前のパートの最初の文の結果として、null
をコレクションに追加しないでください。
コレクションを返すメソッドがある場合は、要素が含まれていなくても、常にCollectionオブジェクトを返すようにしてください。
_
you should not use null to signal a special case
_-if(obj != null) { obj.DoSomething(); }
が行うことではありません。このケースは非常に「特別ではない」ので、構文は特に対応する言語で提供されています:obj?.DoSomething();
– Robert Harvey
さて、この時点であなたはnull
参照によって通知された特殊ケースを処理します。
_
Instead of returning null usually throwing an exception is the more appropriate approach. But be careful: do not use exceptions to implement control flow.
_-Factoryメソッドを作成する場合、要求されたオブジェクトを作成できない場合はnullを返すか、例外をスローしますか? – Robert Harvey
例外を投げたいです。
例外をスローしたい場合、例外をキャッチしてアクションを実行すると、「制御フロー」と見なされますか? – Robert Harvey
制御フローの定義方法によって異なります。結局、catchブロックは単なる別の分岐であり、例外をスローすることはそれを選択することです。うまくいけば、あなたはそのレベルで議論したくない...
それを見るより興味深い方法は、ファクトリがその例外をスローする条件にヒットしたときに(即時)呼び出し元が何を実行できるかです。 つまり要求されたIDが存在しない場合、または一部のパラメーターが無効な場合、呼び出し元は何ができますか?
一番下の行で、私(自分用)は制御フローをハッピーデイシナリオとして定義しています。ファクトリに戻ると、これはオブジェクトが正常に作成され、呼び出し側が制限なしにオブジェクトインターフェイスを使用できることを意味します。
例外をスローする方がよいというアサーションは、Null Reference Exceptionを発生させるだけで簡単に処理できます。 – Robert Harvey
これは問題の根本的な原因を隠すでしょう。このNPEが発生する理由を見つけるには、追加の調査が必要になります。デバッグセッションはかなり可能性があります。
ファクトリーが例外をスローする場合、問題が発生した場所に問題はありません。ファクトリは、意味のある例外タイプを使用したり、この例外が発生した理由を説明したりできます。エラーメッセージ/スタックトレースを確認するだけで、デバッグセッションなしで問題を解決できる可能性が高くなります。