13


6

nullオブジェクトと 空のオブジェクト

[これはhttps://stackoverflow.com/questions/1626597/best-practice-should-functions-return-null-or-an-empty-objectの結果です。関数はnullまたはanを返すべきです。空のオブジェクト?]しかし、私は非常に一般的になろうとしています。 ]

私がこれまで見たことのある(um …​ production)Cコードの多くでは、テストポインタに*たくさん*の_NULL_(または同様の)チェックを書く傾向があります。 NULL-checkを追加すると、ポインタの間接参照が原因で発生するクラッシュを簡単に解決できるため、これらの多くはリリースサイクルの終わり近くに追加されます。調査する時間はそれほど多くありません。

これに対抗するために、ポインタを渡すという(はるかに)一般的な手法の代わりに( const)参照パラメータを使用するコードを書き始めました。 ポインタがない、_NULL_をチェックしたくない(実際にnull参照があるという一般的なケースは無視されます)。

C#では、同じC "問題"が存在します。すべての未知の参照を null(` ArgumentNullException`)と照合し、 null`チェックを追加することで NullReferenceException`を素早く修正したいという願望です。

これを防ぐ一つの方法は、空のオブジェクト( String.Empty、` EventArgs.Empty`)を代わりに使うことでそもそもnullオブジェクトを避けることです。 もう1つは、 `null`を返すのではなく、例外をスローすることです。

私はF#を学び始めたばかりですが、その環境ではnullオブジェクトがはるかに少ないようです。 それで、あなたは本当にたくさんの `null`参照を流す必要はないのでしょうか?

私はここで間違った木に吠えていますか?

11 Answer


25


NullReferenceExceptionを回避するためだけにnull以外の値を渡すことは、はるかに微妙な、デバッグが難しい問題(「スタックを何度か呼び出す」)のための、簡単で解決しやすい問題(「nullなので爆発します」)を交換します。はるかに早くそれが意味のある情報を持っていないがnullではない何らかのオブジェクトを手に入れたからです。

NullReferenceExceptionは*すばらしいこと*です。 それは難しい、大声で、速く失敗します、そしてそれは識別して修正することがほとんどいつも速くて簡単です。 私のお気に入りの例外です。見たときにわかることですが、作業にかかる時間はわずか2分です。 これとは対照的に、再現して原因を突き止めなければならない奇妙な行動を説明しようとする混乱したQAまたは顧客レポートと比較してください。 やあ

メソッドやコードの一部として、あなたを呼んだコードについて合理的に推論できるのは、すべてあなた次第です。 あなたがnull参照を手渡されていて、呼び出し元がnullで何を意味しているのかを合理的に推測することができれば(例えば、空のコレクションなのでしょうか) ただし、nullをどうするか、または呼び出し元がnullで何を意味するのかを合理的に推測できない場合(たとえば、呼び出しコードがファイルを開くように指示して位置をnullとして指定するなど)は、ArgumentNullExceptionをスローする必要があります。 。

このような適切なコーディング方法をすべての「ゲートウェイ」のポイント(コード内の機能の論理的な境界)で維持する - NullReferenceExceptionsははるかにまれです。


7


私はたくさんのNULLを持つコードには戸惑いがちで、可能であれば例外や空のコレクションなどを使ってそれらをリファクタリングしようとします。

Martin Fowlerのhttps://rads.stackoverflow.com/amzn/click / com / 0201485672 [リファクタリング](260ページ)にある "ヌルオブジェクトの紹介"パターンも役に立ちます。 Nullオブジェクトは、実際のオブジェクトが実行するすべてのメソッドに応答しますが、「正しいことをする」という方法で応答します。 したがって、常にOrderをチェックしてorder.getDiscountPolicy()がNULLであるかどうかを確認するのではなく、これらの場合はOrderにNullDiscountPolicyがあることを確認してください。 これにより、制御ロジックが簡素化されます。


4


ヌルは私の投票を得ます。 繰り返しますが、私は「フェイルファスト」の考え方です。

String.IsNullOrEmpty(…​)も非常に有用です、私はそれがどちらかの状況を捕らえると思います:nullまたは空のストリング あなたが渡しているすべてのクラスに対して同様の関数を書くことができます。


3


エラー状態として `null`を返すコードを書いているのなら、しないでください:一般的に、代わりに例外を投げるべきです - 見逃すことははるかに困難です。

あなたがnullを返す可能性があることを恐れているコードを消費しているなら、それからそれらはhttp://blogs.msdn.com/ericlippert/archive/2008/09/10/vexing-exceptions.aspx [骨頭例外]です: Debug.Assertは開発者の間に出力をセンスチェックするために呼び出し側をチェックします。 実運用では_vast_個の null`チェックを_必要_すべきではありませんが、予測できないほど多くの null`を返すライブラリがある場合は、必ず確認してください。

4.0では、コード規約を見たいと思うかもしれません。これにより、「この引数はnullとして渡さない」、「この関数はnullを返さない」などと言って、静的解析中にこれらの要求をシステムに検証させることができます。 あなたが構築するとき)。


2


nullについてのことは意味がないということです。 それは単なる物の欠如です。

ですから、もしあなたが本当に空の文字列/ collection / whateverを意味するのであれば、常に関連するオブジェクトを返し、決して `null`はしないでください。 問題の言語で指定できる場合は、それを指定してください。

静的型では指定できない値を意味する何かを返したい場合は、いくつかのオプションがあります。 「null」を返すことは1つの答えですが、意味がないと少し危険です。 例外を投げることは、実際にはあなたが意味することかもしれません。 特殊な場合(おそらく多態性、つまり特殊な場合のパターン(特殊な場合はNullオブジェクトパターン))で型を拡張することができます。 戻り値をもっと意味のある型にラップすることをお勧めします。 あるいは、コールバックオブジェクトを渡したいかもしれません。 通常はたくさんの選択肢があります。


1


私はそれが依存すると言うでしょう。 単一のオブジェクトを返すメソッドの場合、通常はnullを返します。 コレクションを返すメソッドでは、通常空のコレクション(null以外)を返します。 ただし、これらは規則よりもガイドラインに沿っています。


1


もしあなたが "null"のない環境でプログラムしたいのであれば、もっと頻繁に拡張メソッドを使うことを検討してください。それらはNullReferenceExceptionsの影響を受けず、少なくとも "null"はもう存在しないようにふります:

public static GetExtension(this string s)
{
    return (new FileInfo(s ?? "")).Extension;
}

これは次のように呼び出すことができます。

// this code will never throw, not even when somePath becomes null
string somePath = GetDataFromElseWhereCanBeNull();
textBoxExtension.Text = somePath.GetExtension();

私は知っています、これは単に便利さであり、多くの人がそれをオブジェクト指向の原則の違反と正しく考えています。しかし、それはまた別の話です。 編集:ダンはビルワーグナー(より効果的なC#)はそれが悪い習慣だと考えていると述べ、彼は正しいです。 これまで `IsNull`拡張メソッドを考えました;-)?

コードを読みやすくするために、別のヒントがあります。オブジェクトがnullのときにデフォルトを指定するには、null合体演算子を使用することがより頻繁にあります。

// load settings
WriteSettings(currentUser.Settings ?? new Settings());

// example of some readonly property
public string DisplayName
{
    get
    {
         return (currentUser ?? User.Guest).DisplayName
    }
}

これらのどれもが null`を時々チェックすることはありません(そして ??`は隠れたif分岐以外の何ものでもありません)。 私は、コードを読みやすくすると考えているからです。 私のコードが `null`のためのifステートメントで散らかっているとき、私はデザインに何か問題があることを知っていて、私はリファクタリングします。 私は誰でも同じことをするように勧めます、しかし私は意見が問題に関して乱暴に変わることを知っています。

(更新)例外との比較

これまでの説明で言及されていないのは、例外処理との類似点です。 邪魔になると思うときはいつでもどこでも `null`を無視することに気づいたら、それは書くことと基本的に同じです:

try
{
    //...code here...
}
catch (Exception) {}

これは、例外の痕跡を削除してそれを見つけるためだけの効果があり、コードのかなり後の方で無関係の例外が発生します。 このスレッドで前述したように、私は `null`の使用を避けるのが良いと思いますが、例外的なケースに対してnullを持つことは良いことです。 null-ignore-blocksでそれらを隠さないでください、それはcatch-all-exceptionsブロックと同じ効果を持つことになります。


1


例外的な主人公たちのために彼らは通常トランザクションプログラミングと強力な例外安全保証または盲目のガイドラインから生じます。 まともな複雑さで、すなわち。 非同期ワークフロー、I / O、特にネットワーキングコードは単純に不適切です。 CでGoogleスタイルの文書が問題になっているのを見た理由、それにすべての良い非同期コードが「それを強制していない」(あなたの好きなマネージドプールも考えてください)

それにはもっとあります、そしてそれは単純化のように見えるかもしれませんが、それは本当にそれほど単純です。 1つには、大量の例外を使用するように設計されていないもので、多くの例外が発生します。 とにかく私が言いたいのは、これを世界のトップ図書館デザイナーから読んだ、いつもの場所はブーストです(音楽ソフトウェアを書かなければならなかったので例外を愛する他のキャンプと混同しないでください:-)。

あなたの例では、そしてこれはFowlerの専門知識ではありません、効率的な '空のオブジェクト’イディオムはCでのみ可能なキャストメカニズムのために可能です(おそらく確かに_dominance_によってではない)。 一方、null _type_では、クリーンな呼び出しサイトとコードの構造を維持しながら、例外をスローして任意のことを実行できます。

C#では、あなたの選択は、良いか不正な型の単一のインスタンスになることができます。そのようなものとして、それは受け入れを投げるか、または単にそのまま実行することができます。 そのため、他の規約に違反する可能性があります。

結局のところ、それはコールサイトをクリーンアップします、しかしあなたが多くのライブラリとの衝突に直面することを忘れないでください(そして特にコンテナ/辞書からのリターン、エンドイテレータは頭に浮かぶ、および他のどんな外部インタフェースへのコード) 値としてnullをチェックすることは非常に最適化されたマシンコードであり、覚えておくべきことですが、制約、参照などを理解することなく、いつでもワイルドポインタの使用法に同意することになります。 。

付け加えると、銀の弾丸はありません、そして管理空間でnull参照をクラッシュさせたり、null参照を使用したり、例外を投げたり処理しなかったりすることはまったく同じ問題です。 どんな適切な環境でもそれらからの保護を提供し(あなたが望む任意のOSに任意のフィルタをインストールすることができます、あなたが他にVMが行うと思います) グーグルからのx86検証に再び参加してください。はるかに速くてより良い 'IL'、 '動的な’友好的なコードなどをする彼ら自身の方法です。

これにあなたの本能で行きなさい、賛否両論を重くし、そして効果を局所化しなさい。 将来的には、コンパイラはこれらすべてのチェックを最適化し、実行時やコンパイル時のヒューマンメソッドよりはるかに効率的にします(ただし、モジュール間のやり取りにはそれほど簡単ではありません)。


1


可能な限りメソッドからnullを返さないようにします。 一般に2種類の状況があります - nullの結果が合法である場合と、決して起こらないはずの場合です。

最初のケースでは、結果が合法でない場合、それらに関連するnullの結果とnullのチェックを回避するために利用可能ないくつかの解決策があります:http://www.codinghelmet.com/?path=howto/reduce-cyclomatic-complexity- null-object [Null Object pattern]および 特別なケースパターンは、何もしない代替オブジェクトを返すためのものです。特定の状況下での特定のもの

オブジェクトを返さないことが合法的であるが、それでもNull ObjectやSpecial Caseの観点から適切な代替物がない場合、私は通常http://www.codinghelmet.com/?path=howto/reduce-cyclomatic-complexityを使用します。 -option-functional-type [オプション機能タイプ] - 正当な結果が得られない場合は、空のオプションを返すことができます。 空のオプションを処理するための最良の方法が何であるかを見るのはクライアント次第です。

最後に、何かが欠けているとメソッドが結果を生成できないという理由で、メソッドからオブジェクトを返すことが正当でない場合は、例外をスローしてそれ以上実行しないようにします。


0


'empty’は常に定義されているわけではないので、常に空のオブジェクトを返すことはできません。 たとえば、int、float、またはboolが空になるとはどういう意味ですか?

NULLポインタを返すことは必ずしも悪い習慣ではありませんが、(const)参照を返すことがより良い習慣だと思います(もちろんそうすることが理にかなっている場合)。

そして最近私はFallibleクラスをよく使いました。

立派なtheName = obj.getName(); if(theName){// ... }
http://code.google.com/p/xulwin/source/browse/trunk/XULWin/include/XULWin/Fallible.h[私も作成しました。自分の]。