削除するアイテムが指定されていない場合、サービスは例外をスローするかリターンする必要があります


12

次のように表すことができるコードがあります:

public class ItemService {

    public void DeleteItems(IEnumerable<Item> items)
    {
        // Save us from possible NullReferenceException below.
        if(items == null)
            return;

        foreach(var item in items)
        {
            // For the purpose of this example, lets say I have to iterate over them.
            // Go to database and delete them.
        }
    }
}

今、私はこれが正しいアプローチなのか、それとも例外を投げるべきなのか疑問に思っています。返すことは空のコレクションを反復することと同じであるため、例外を避けることができます。つまり、重要なコードは実行されませんが、一方でコードのどこかに問題を隠している可能性がありますパラメータ?これは、コードのどこかに問題があることを示している可能性があります。DeleteItemsnull

これは私が通常サービスのメソッドで抱えている問題です。それらのほとんどは何かをして結果を返さないため、誰かが無効な情報を渡すと、サービスが行うことは何もないので戻ります。


2
これは私の個人的な意見ですが、意図しない(例外的な)ことを行う場合、メソッドは例外をスローする必要があることを常に理解していると感じています。あなたの場合、誰かがnull / 0アイテムを削除しようとすると、InvalidOperationExceptionがスローされます。
ファルガンティル


1
@gnat私の質問は、サービスの使用方法とその目的のため、特にサービスに関するものです。この「重複」は一般的なケースについてのみ言及しており、主な答えは私の正確な方法の文脈では矛盾しています。
FCin

2
enumerableをnullではなく空にすることは可能ですか?別の方法で処理しますか?
JAD

13
@Falgantil nullが例外に値することはわかりますが、空のリストに例外をスローするのはばかげていると思います。
ケビン

回答:


58

これらは2つの異なる質問です。

受け入れnullますか?それはnull、コードベースに関する一般的なポリシーに依存します。私の意見でnullは、明示的に文書化されている場合を除き、すべての場所を禁止することは非常に良い習慣ですが、コードベースが既に持っている慣習に従うことはさらに良い習慣です。

空のコレクションを受け入れますか?私の意見では:はい、絶対に。すべての呼び出し元を空でないコレクションに制限するのは、数学的に正しいことをするよりもはるかに労力がかかります-ゼロという概念に疑問を抱いている一部の開発者を驚かせても。


9
いずれにせよ、スローする場合、コードをまったく作成せずAhaINoticedYouPassingInNullExceptionに、ランタイムが既にスローする機能を提供している場合、スローにはそれほど多くのユーティリティはありませんNullReferenceException。いくつかのユーティリティがあります(たとえば、前者のケースではnullを渡すが、後者ではないのにユニットテストがあるかどうかをコードカバレッジツールが教えてくれます)それは通常プログラマーのエラーだからです。
スティーブジェソップ

2
...予想される場合に渡すものがnullであることがわかっており、呼び出している関数を検証するために積極的に必要な場合は、大ざっぱなパラメーターのために発生した例外をすぐにキャッチする必要があります。Kilianが答えで述べているように、一般的なポリシーは、明示的に許可されていないすべての場所でnullを禁止することです。そして、あなたはキャッチしていないでしょうNullReferenceExceptionか、AhaINoticedYouPassingInNullExceptionどこでも高レベルの災害復旧コードを除きます。そして、その例外ハンドラーはおそらくバグレポートを作成する必要があります:
スティーブジェソップ

2
@FCin:インターフェース設計は、ユーザーが実際にサービスから必要とするものと一致する必要があります。したがって、すべての呼び出し元がこれをtry / catchする場合、このメソッドまたはそれと一緒の便利なメソッドのいずれかがnullをチェックして無視する必要があります。ただし、Kilianが言うように、通常、nullの伝播をプログラミングエラーとして扱い、すべての呼び出しサイトで続行しようとしない方が適切です。さらに言えば、このメソッドが呼び出されるサービスがnull-コントローラーにその場合でもキャッチして継続するための定型句が含まれているとしたらどうでしょうか?
スティーブジェソップ

2
...その場合、コードベースは、低レベルで処理して続行する必要がある条件として、欠落しているコンポーネントを一貫して処理しているように見えます。「この操作を進めるために、サービスと列挙可能なものを提供するのは誰の責任ですか?」と合理的に尋ねることができます。答えが「誰か」である場合、関数は前提条件をチェックしており、失敗した前提条件の結果は、通常、すべての呼び出しではなく、高レベルでキャッチされた例外になります。操作に必要なものが本当にオプションである場合、関数は予想されるユースケースを処理しています
スティーブジェソップ

2
明示的にスローすることArgumentNullExceptionは、内部コードでスローすることよりも優れていますNullReferenceException-例外メッセージを純粋に見ると、スローサイトのメソッド本体の論理エラーではなく入力エラーであることが明らかであり、早期終了は後で予期しないヌルから部分的に変更されるのではなく、他のデータが有効な状態のままになる可能性が高くなります。
ミラル

9

ヌル値

@KilianFothが既に言ったように、あなたの一般的な方針に固執してください。それがnull空のリストの「速記」として扱う場合は、そのようにします。

null値に関する一貫したポリシーがない場合は、次のものをお勧めします。

null「通常」タイプでは表現できない状況を表すために予約する必要があります。例えば、null「わからない」を表すために使用します。そして、この値を不注意に使用しようとするすべての人が例外を取得するため、これは正しい選択です。これは正しいことです。

null空のリストの省略形として使用しても、要素がゼロのリストである完全な表現がすでに存在するため、その方法は限定されません。また、リストを処理するコードのすべての部分で有効な略記をチェックすることを強制するため、技術的に悪い選択ですnull

空のリスト

以下のためDeleteItems()の方法、空のリストを渡すと、効果的に何もしないことを意味します。私はそれを引数として許可し、例外をスローするのではなく、ただすぐに戻ります。

もちろん、呼び出し元は最初にゼロ要素をチェックしDeleteItems()、その場合は呼び出しをスキップできます。Web APIについて話している場合、効率上の理由から、呼び出し側不必要なトラフィックと往復遅延を避けるために実際にそれを行う必要があります。しかし、私はあなたのAPIがそれを強制するべきではないと思います。


1

例外をスローし、呼び出し元のコードでnullを処理します。

設計ルールとして、パラメーター値としてnullを避けるようにしてください。nullは実際には例外であるため、一般にNullPointerExceptionsを減らします。

それに加えて、残りのコードを見てください。これがプロジェクトで一般的なパターンである場合、一貫性を保ちます。


私は自分のサービスがどのように構成されているかを「整形」しているので、無効な入力をスローするためにリファクタリングが必要になる場合がありますが、多くは必要ありません。しかし、nullは非表示ではなくスローを開始すると例外になるという点に同意します。
FCin

0

一般的に、例外のスローは例外的な状況、つまり、コードに現在のコンテキストで実行する適切なアクションがない場合のために予約する必要があります。

この思考プロセスをこの状況に適用できます。これがパブリックメソッドを持つパブリッククラスであることを考えると、パブリックAPIがあり、理論的には何が渡されるかを制御することはできません。

あなたのコードは、それを呼び出しているものについてのコンテキストを持たない(そうすべきではない)ため、null値で合理的にできることは何もない。これは確かに候補ですArgumentNullException


「現在のコンテキストで実行する合理的なアクションコースがコードにない場合」しかし、私の考えでは、それは無効を返すという合理的な行動方針を持っています。空のコレクションを渡すときとまったく同じことを行うため、空のコレクションを許可した場合、なぜ許可しないのでしょうかnull
FC

1
@FCin空のコレクションのため、空であることがわかります。ではnull、あなたは何のコレクションを持っていません。呼び出し元のコードがメソッドにコレクションを提供することになっている/予想される場合は、何か問題があるため、スローする必要があります。nullを空のコレクションと同様に扱う唯一の理由は、呼び出し元のコードがnullコレクションを生成することを合理的に期待できる場合です。他の回答が指摘したように、ほとんどの場合、何かがnull異常です。これらを空のコレクションと同じように扱うと、コードの他の場所の問題を潜在的に無視することになります。
JAD

@FCin:また、null空を意味する場合、このメソッドに固有のコンテキストです。同じコードベースのどこかで、何らかのフィルター処理を行うIEnumerableor IContainerパラメーターがあり、null「フィルターなし」(すべてを許可)を意味し、空のフィルターは何も許可しないことを意味します。そして別の場所でnullは、「わからない」を明示的に意味するかもしれません。したがって、それnullが常にどこでもまったく同じことを意味するわけではないことを考えると、その意味が必要であるか、少なくとも誰かにとって有用でない限り、その意味をまったく発明しないことをお勧めします。
スティーブジェソップ

0

この質問は例外に関するものではなくnull、有効な議論であることのようです。

そのため、何よりもまず、nullそのメソッドの引数に許容値かどうかを判断する必要があります。そうであれば、例外は必要ありません。そうでない場合は、例外が必要です。

許可するかどうかnullは、多くのGoogleヒットが示すように、議論の余地があります。つまり、明確な答えを得ることができず、あなたが働いている場所での意見と伝統次第です。

競合の別の領域は、誤ったパラメーターを「修正」しようとしていない限り、ライブラリー関数がそのようなことについて本当に厳しいか、または可能な限り寛大であるかどうかです。これを、ネットワークプロトコル、メール転送などの世界と比較してください(プログラミングのメソッドと同様に、インターフェイスコントラクトです)。そこでは、通常、送信者はプロトコルにできるだけ厳密に従う必要がありますが、受信者は邪魔にならないようにして、何でも処理できるようにするというポリシーがあります。

決定する必要があります:null処理のようなかなり大きなポリシーを強制するのは、実際にはライブラリメソッドの仕事ですか?特に、ライブラリが他の人(異なるポリシーを持っている人)によっても使用されている場合。

他の同様のコード(「ライブラリ」スタイルのコード)の99%が別の方法で行わない限りnull、値を許可し、そのセマンティクスを定義および文書化する(つまり、null = empty arrayこの場合)側で間違いを犯し、例外をスローしません'円形。


1
「null処理のようなかなり大きなポリシーを実施するのは、本当にライブラリメソッドの仕事ですか?」-ダウン嘘を考えてのラインがアサートとにあなたをできるように、デバッグモードで、その助けそれはあなたが何をしたいなら、ポリシーを適用するのではなく、実際にそれを強制し。そのnullため、受け入れるものをリベラルにするという原則に基づいて飲み込む場合は、ログインすることや投げることは、渡すことを厳しくするつもりであるが、コードとコードの両方を台無しにしている人々に役立ちますnullとにかく合格する範囲でユニットテストを行います。
スティーブジェソップ

@SteveJessop、あなたが私の答えの精神に反対しているのか、それともあなたがその方針に沿って続けているのか、本当に分かりません。とはいえ、単に飲み込むことを提案するのではなくnull、メソッドの契約でそれが受け入れられることを公然と宣言すること(または、OPが出てくるソリューションに応じてそうでないこと)を宣言してから、例外をスローするかどうかの質問(またはそうでない)それ自体を解決します。
AnoE
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.