関数はnullまたは空のオブジェクトを返す必要がありますか?


209

関数からデータを返すときのベストプラクティスは何ですか。Nullまたは空のオブジェクトを返す方が良いですか?そして、なぜ一方を他方に対して行うべきなのでしょうか?

このことを考慮:

public UserEntity GetUserById(Guid userId)
{
     //Imagine some code here to access database.....

     //Check if data was returned and return a null if none found
     if (!DataExists)
        return null; 
        //Should I be doing this here instead? 
        //return new UserEntity();  
     else
        return existingUserEntity;
}

このプログラムでは、データベースにそのGUIDのユーザー情報がないという有効なケースがあると仮定してみましょう。この場合、例外をスローすることは適切ではないと思いますか?また、例外処理がパフォーマンスを低下させる可能性があるという印象も受けています。


4
と思いますif (!DataExists)
サラベッセル

107
これはアーキテクチャ上の問題であり、完全に適切です。OPの質問は、OPが解決しようとしているビジネス上の問題に関係なく有効です。
ジョセフフェリス

2
この質問はすでに十分に回答されています。これは非常に興味深い質問だと思います。
John Scipione、

「getUser()」はnullを返す必要があります。「getCurrentUserInfo()」または「getCurrentPermissions()」、大藤は、より多くの暴露の質問だろう-彼らは返す必要がありますnull以外の回答オブジェクトを関係なく、誰/または誰がログインしているかどうか。
トーマス・W

2
@Bergiいいえ、もう1つは重複しています。鉱山は最初に尋ねられました、10月に、他は3ガ後に12月に尋ねられました。さらに、もう1人は少し異なるコレクションについて話します。
7wp 2017年

回答:


207

データが利用できないことを示す場合は、通常、nullを返すのが最善の方法です。

空のオブジェクトはデータが返されたことを意味しますが、nullを返すと、何も返されなかったことを明確に示します。

さらに、オブジェクトのメンバーにアクセスしようとすると、nullを返すとnull例外が発生します。これは、バグのあるコードを強調表示するのに役立ちます。何もないメンバーにアクセスしようとしても意味がありません。空のオブジェクトのメンバーへのアクセスは失敗せず、バグが発見されない可能性があります。


21
問題を飲み込んでnullを返すのではなく、例外をスローする必要があります。少なくとも、これをログに記録してから続行してください。
Chris Ballance、

130
@Chris:同意しない。コードが戻り値がnullであることを明確に文書化している場合、基準に一致する結果が見つからなければnullを返すことは完全に許容されます。例外のスローは、最初の選択ではなく、最後の選択でなければなりません。
マイクホファー、

12
@クリス:何に基づいてこれを決めますか?方程式にロギングを追加することは確かに過剰に思われます。ユーザーがいない場合に、使用するコードが何をすべきかを決定します。以前のコメントと同様に、「データなし」として普遍的に定義されている値を返すことにはまったく問題はありません。
アダムロビンソン

17
Microsoft開発者が「nullを返す」ことは「問題を飲み込む」ことと同じだと信じていることに少し困惑しています。メモリが機能する場合、フレームワークにはトンメソッドがあり、呼び出し元の要求に一致するものがなければメソッドnullが返されます。それは「問題を飲み込んでいる」のですか?
マイク・ホーファー

5
最後にbool GetUserById(Guid userId, out UserEntity result)重要なことですが、 "null"の戻り値よりも優先され、例外をスローするほど極端ではありません。それはのような美しくnull自由なコードを可能にしますif(GetUserById(x,u)) { ... }
Marcel Jackwerth、2009年

44

それはあなたのケースにとって最も意味のあるものに依存します。

たとえば、「そのようなユーザーは存在しません」など、nullを返すことは意味がありますか?

または、デフォルトのユーザーを作成することは理にかなっていますか?これは、ユーザーが存在しない場合、呼び出し元のコードは、ユーザーが要求したときに存在することを意図していると安全に想定できる場合に最も意味があります。

または、呼び出し側のコードが無効なIDを持つユーザーを要求している場合に例外(「FileNotFound」と呼ばれる)をスローすることは理にかなっていますか?

ただし、懸念/ SRPの観点から、最初の2つはより正確です。そして、技術的には最初は(だけ髪で)最も正確である-ユーザーを取得- GetUserByIdは一つだけに責任を負わなければなりません。独自の「ユーザーが存在しない」ケースを別の何かを返すことで処理すると、SRPの違反になる可能性があります。別のチェックに分ける- bool DoesUserExist(id)例外をスローすることを選択した場合は適切です。

以下の広範なコメントに基づく:これがAPIレベルの設計問題である場合、このメソッドは「OpenFile」または「ReadEntireFile」に類似している可能性があります。リポジトリからユーザーを「オープン」し、結果のデータからオブジェクトをハイドレイトしています。この場合、例外適切な場合があります。そうではないかもしれませんが、そうかもしれません。

すべてのアプローチが受け入れ可能です-それは、API /アプリケーションのより大きなコンテキストに基づいて、単に依存します。


誰かがあなたに反対票を投じ、私はあなたに反対票を投じました。例外:ポスターが提供するようなメソッドでユーザーが見つからない場合でも、例外をスローしません。投げ方法等、IDから来たことを場所についての詳細を知る必要がある-何のユーザーを見つけていないことは、無効なIDまたはそのようないくつかの例外に値する問題を暗示している場合、それは、最大高起こるべき
ジェイコブMattison

(私は、反対票がこのような状況で例外をスローするという考えへの反対であったと思います。)
Jacob Mattison '26

1
最後のポイントまで同意しました。「データなし」として普遍的に定義されている値を返すことによるSRPの違反はありません。これは、where句が結果を生成しない場合にSQLデータベースがエラーを返す必要があることを示すようなものです。Exceptionは有効なデザインの選択ですが(消費者として私を苛立たせますが)、nullを返すことほど「より正確」ではありません。いいえ、私はDVではありません。
アダムロビンソン

@JacobM存在しないファイルシステムパスを要求したときに例外をスローし、nullを返さないが、データベースからは返さない。明らかに両方が適切であり、これが私が達成していることです-それは単に依存します。
Rex M

2
@Charles:「ある時点で例外がスローされるべきか」という質問に答えていますが、問題は「この関数が例外をスローすべきか」ということです。正解は「はい」ではなく「たぶん」です。
アダムロビンソン

30

個人的には、NULLを使用しています。返すデータがないことは明らかです。ただし、Nullオブジェクトが役立つ場合もあります。


これを自分自身の答えとして追加しようとしています。NullObjectPatternまたは特殊なケースパターン。そして、あなたは1ケースごとに、NoUserEntitiesFound、NullUserEntitiesなどを実装することができ
デヴィッドSwindells

27

戻り値の型が配列の場合は空の配列を返し、それ以外の場合はnullを返します。


リストの0アイテムは、現時点で割り当てられていないリストと同じですか?
AnthonyWJones

3
リストの0アイテムはと同じではありませんnull。それforeachを心配することなく、ステートメントやlinqクエリで使用できますNullReferenceException
Darin Dimitrov、

5
これ以上賛成されていないことに驚いています。これはかなり合理的なガイドラインのようです。
2012

まあ、空のコンテナはnullオブジェクトパターンの特定のインスタンスにすぎません。どちらが適切かはわかりません。
Deduplicator

データが利用できないときに空の配列を返すのは単に間違っています。利用可能でアイテムを含まないデータと利用できないデータには違いがあります。どちらの場合も空の配列を返すと、どちらが正しいかを知ることができなくなります。あなたはデータも存在するかどうかをチェックせずにforeachのを使用することができるだけので、それを行うことは愚かを超えている-呼び出し側は、必要がある呼び出し側がチェックされていない場合は、データが存在するかどうかをチェックし、とNullReferenceExceptionする必要が良い、それはバグを公開しているので...
モニカを

12

特定のコントラクトが壊れている場合は、(のみ)例外をスローする必要があります。
特定の例では、既知のIDに基づいてUserEntityを要求することは、失われた(削除された)ユーザーが予期されるケースであるかどうかに依存します。その場合は戻りますnullが、それが予期されたケースでない場合は例外をスローします。
関数が呼び出された場合、UserEntity GetUserByName(string name)おそらくスローせず、nullを返すことに注意してください。どちらの場合も、空のUserEntityを返すことは役に立ちません。

文字列、配列、コレクションの場合、状況は通常異なります。メソッドがnull「空の」リストとして受け入れなければならないが、長さゼロのコレクションを返すというMSのガイドラインを覚えていますnull。文字列についても同じです。空の配列を宣言できることに注意してください。int[] arr = new int[0];


空の文字列を返すかどうかを決定しているときにGoogleがこれを示したので、文字列が異なると述べてよかったです。
Noumenon 2013年

文字列、コレクションと配列がありません異なります。MSがそう言った場合、MSは間違っています。空の文字列とnullの間、および空のコレクションとnullの間には違いがあります。どちらの場合も、前者は既存のデータ(サイズ0)を表し、後者はデータの欠如を表します。場合によっては、区別が非常に重要です。たとえば、キャッシュ内のエントリを検索する場合、キャッシュされているが空であるデータとキャッシュされていないデータの違いを知りたいので、基礎となるデータソースからデータをフェッチする必要があります。空の。
2015

1
ポイントとコンテキストを逃しているようです。.Wher(p => p.Lastname == "qwerty")ではなく空のコレクションを返す必要がありnullます。
Henk Holterman、2015

@HenkHolterman完全なコレクションにアクセスして、コレクション内のアイテムを受け入れないフィルターを適用できる場合、空のコレクションが正しい結果です。しかし、完全なコレクションが存在しない場合、空のコレクションは非常に誤解を招きやすい-状況が正常か例外かによって、nullまたはスローは正しいでしょう。あなたの投稿はあなたが話している状況を限定していなかったので(そして今、あなたは前者の状況について話していることを明確にしました)、OPが後者の状況について話しているので、私はあなたに同意しなければなりません。
モニカを2015

11

これはビジネス上の問題であり、特定のGuid Idを持つユーザーの存在がこの機能の予想される通常の使用例であるか、またはこのメソッドがユーザーに提供する機能をアプリケーションが正常に完了できない異常なのかによって異なります対象...

それが「例外」である場合、そのIDを持つユーザーが存在しないと、アプリケーションが実行している機能をアプリケーションが正常に完了できなくなります(製品を出荷した顧客の請求書を作成しているとします... )、この状況ではArgumentException(またはその他のカスタム例外)がスローされます。

行方不明のユーザーに問題がない場合(この関数を呼び出した場合の通常の結果の1つ)、nullを返します。

編集:(別の回答でアダムからのコメントに対処するため)

アプリケーションに複数のビジネスプロセスが含まれ、そのうちの1つ以上が正常に完了するためにユーザーを必要とし、1つ以上がユーザーなしで正常に完了することができる場合、例外はコールスタックのさらに上方のどこにスローされる必要があります。ユーザーを必要とするビジネスプロセスがこの実行スレッドを呼び出しています。このメソッドとそのポイント(例外がスローされる場所)の間のメソッドは、ユーザーが存在しない(null、booleanなど、これは実装の詳細です)ことを伝えるだけです。

しかし、アプリケーション内のすべてのプロセスがユーザーを必要とする場合でも、このメソッドで例外をスローします...


-1で反対投票者、+ 1でチャールズ-これは完全にビジネス上の問題であり、ベストプラクティスはありません。
オースティンサローネン

それは小川を渡っています。「エラー状態」であるかどうかは、ビジネスロジックによって決まります。これをどのように処理するかは、アプリケーションアーキテクチャの決定です。ビジネスロジックは、nullが返されることを要求するのではなく、要件が満たされるだけです。ビジネスがメソッドの戻り値の型を決定している場合、それらは実装の技術的な側面に関与しています。
ジョセフフェリス

@joseph、「構造化例外処理」の背後にある中心的な原則は、実装するためにコーディングされた機能をメソッドが完了できない場合に例外がスローされることです。このメソッドが実装するようにコード化されたビジネス関数を「正常に完了」できる場合(ドメインモデルで何を意味する場合でも)、例外をスローする必要はなく、nullを返すことができます。 、またはブール値の "FoundUser"変数、または何でも...ユーザーが見つからなかったという呼び出しメソッドへの通信方法は、技術的な実装の詳細になります。
Charles Bretana、2009年

10

個人的にはnullを返します。これは、DAL /リポジトリレイヤーが機能することを期待しているためです。

それが存在しない場合は、オブジェクトを正常にフェッチすると解釈できるものを返さないでnullください。ここでは問題なく機能します。

最も重要なことは、DAL / Reposレイヤー全体で一貫性を保つことです。これにより、DAL / Reposレイヤーの使用方法について混乱することがなくなります。


7

私はする傾向があります

  • return nullそれはかどうかを事前に知られていないときにオブジェクトIDが存在しない場合にすべき存在。
  • throw存在するはずのオブジェクトIDが存在しない場合。

これら2つのシナリオを、これら3つのタイプの方法で区別します。最初:

Boolean TryGetSomeObjectById(Int32 id, out SomeObject o)
{
    if (InternalIdExists(id))
    {
        o = InternalGetSomeObject(id);

        return true;
    }
    else
    {
        return false;
    }
}

第二:

SomeObject FindSomeObjectById(Int32 id)
{
    SomeObject o;

    return TryGetObjectById(id, out o) ? o : null;
}

第三:

SomeObject GetSomeObjectById(Int32 id)
{
    SomeObject o;

    if (!TryGetObjectById(id, out o))
    {
        throw new SomeAppropriateException();
    }

    return o;
}

そうではoutないref
マットエレン

@マット:はい、サー、私は確かにそうします!修繕。
Johann Gerell、

2
本当に、これが唯一の万能の答えであり、したがって、完全かつ唯一の真実です!:)はい、それはメソッドの呼び出しに基づく仮定に依存します...したがって、最初にこれらの仮定をクリアしてから、上記の適切な組み合わせを選択します。ここにたどり着くまでにスクロールしすぎた:) +100
yair

これは、非同期メソッドをサポートしていないことを除いて、使用するパターンのようです。私はこの回答を参照し、タプルリテラルを使用した非同期ソリューションを追加しました-> ここ
tugates

6

さらに別のアプローチでは、値を操作するコールバックオブジェクトまたはデリゲートを渡します。値が見つからない場合、コールバックは呼び出されません。

public void GetUserById(Guid id, UserCallback callback)
{
    // Lookup user
    if (userFound)
        callback(userEntity);  // or callback.Call(userEntity);
}

これは、コード全体でnullチェックを回避したい場合や、値が見つからないことがエラーではない場合に役立ちます。特別な処理が必要な場合は、オブジェクトが見つからない場合のコールバックを提供することもできます。

public void GetUserById(Guid id, UserCallback callback, NotFoundCallback notFound)
{
    // Lookup user
    if (userFound)
        callback(userEntity);  // or callback.Call(userEntity);
    else
        notFound(); // or notFound.Call();
}

単一のオブジェクトを使用する同じアプローチは次のようになります。

public void GetUserById(Guid id, UserCallback callback)
{
    // Lookup user
    if (userFound)
        callback.Found(userEntity);
    else
        callback.NotFound();
}

設計の観点からは、私はこのアプローチが本当に好きですが、ファーストクラスの関数を容易にサポートしない言語で呼び出しサイトを大きくするという欠点があります。


面白い。デリゲートについて話し始めたとき、私はすぐにラムダ式をここで使用できるかどうか疑問に思い始めました。
7wp 2009年

うん!私が理解しているように、C#3.0以降のラムダ構文は、基本的に匿名デリゲートの構文シュガーです。同様に、Javaでは、ラムダや匿名デリゲートの構文がなくても、匿名クラスを簡単に作成できます。少し醜いですが、とても便利です。私は、これらの日と仮定し、私のC#の例は、のFunc <UserEntity>またはその代わりという名前のデリゲートのそれのようなものを使用している可能性がありますが、私が上だった最後のC#プロジェクトは、まだバージョン2.使用していた
マルク・

+1私はこのアプローチが好きです。しかし問題は、それが従来のものではなく、コードベースのエントリへの障壁をわずかに増加させることです。
timoxley

4

私たちはCSLA.NETを使用しており、失敗したデータフェッチは「空の」オブジェクトを返すという見方をしています。これは、であるかどうかをチェックする規則を要求するため、実際にはかなり煩わしいobj.IsNewものobj == nullです。

前のポスターで述べたように、戻り値がnullの場合、コードはすぐに失敗し、空のオブジェクトが原因でステルス問題が発生する可能性が低くなります。

個人的にnullはもっとエレガントだと思います。

これは非常に一般的なケースであり、私はここの人々がそれに驚いているように思われることに驚いています。どのWebアプリケーションでも、データはしばしばクエリ文字列パラメーターを使用してフェッチされます。 」

これは次の方法で処理できます。

if(User.Exists(id)){
  this.User = User.Fetch(id);
} そうしないと {
  Response.Redirect( "〜/ notfound.aspx");
}

...しかし、それは毎回データベースへの追加の呼び出しであり、トラフィックの多いページでは問題になる可能性があります。一方:

this.User = User.Fetch(id);

if(this.User == null){
  Response.Redirect( "〜/ notfound.aspx");
}

... 1つの呼び出しのみが必要です。


4

nullnull結合演算子(??)と互換性があるので、私はを好みます。


4

空のオブジェクトの代わりにnullを返すと思います。

しかし、ここで述べた特定のインスタンスは、ユーザーIDでユーザーを検索しています。これは、そのユーザーのキーのようなものです。その場合、ユーザーインスタンスインスタンスが見つからない場合は、例外をスローする必要があります。 。

これは私が一般的に従うルールです:

  • 主キーによる検索操作で結果が見つからない場合は、ObjectNotFoundExceptionをスローします。
  • 他の基準による検索で結果が見つからなかった場合は、nullを返します。
  • 複数のオブジェクトを返す可能性がある非キー条件による検索で結果が見つからない場合は、空のコレクションを返します。

これらのケースのいずれかで例外をスローするのはなぜですか?ユーザーがデータベースに存在しないこともあり、そうならない場合もあります。これは例外的な動作ではありません。
Siride

3

これはコンテキストに応じて異なりますが、(例のように)1つの特定のオブジェクトを探している場合は通常nullを返し、オブジェクトのセットを探しているが何もない場合は空のコレクションを返します。

コードに誤りがあり、nullを返すとnullポインタ例外が発生する場合は、すぐにそれをキャッチするほうがよいでしょう。空のオブジェクトを返す場合、最初の使用は機能しますが、後でエラーが発生する場合があります。


+1私はあなたがここで言っているのと同じロジックに質問していたので、これについて他の意見がどうなるかを確認するために質問を投稿しました
7wp

3

この場合のベストは、そのようなユーザーがいない場合には「null」を返します。また、メソッドを静的にします。

編集:

通常、このようなメソッドは「User」クラスのメンバーであり、そのインスタンスメンバーへのアクセス権はありません。この場合、メソッドは静的でなければなりません。そうでない場合は、「User」のインスタンスを作成してから、別の「User」インスタンスを返すGetUserByIdメソッドを呼び出す必要があります。これは紛らわしいことに同意します。ただし、GetUserByIdメソッドが「DatabaseFactory」クラスのメンバーである場合は、インスタンスメンバーのままにしても問題ありません。


メソッドを静的にする理由を尋ねることはできますか?依存性注入を使用したい場合はどうなりますか?
7wp、2009年

さて、私はあなたの論理を取得します。しかし、私はリポジトリパターンに固執しており、リポジトリには依存関係注入を使用したいので、静的メソッドを使用できません。ただし、nullを返すことを提案する場合は+1 :)
7wp

3

個人的にオブジェクトのデフォルトのインスタンスを返します。その理由は、メソッドが(メソッドの目的に応じて)0から複数、または0から1を返すことを期待しているためです。このアプローチを使用して、それがあらゆる種類のエラー状態になる唯一の理由は、メソッドがオブジェクトを返さず、常に(1対多または特異な戻りに関して)予期されていた場合です。

これがビジネスドメインの問題であるという仮定については、方程式のその側からはわかりません。戻り値の型の正規化は、有効なアプリケーションアーキテクチャの問題です。少なくとも、コーディングプラクティスの標準化が必要です。「シナリオXでは、nullを与えるだけ」と言うビジネスユーザーがいるとは思えません。


+1問題の別の見方が好きです。つまり、基本的に、メソッドがアプリケーション全体で一貫している限り、どのアプローチを選択しても問題ないということです。
7wp、

1
それが私の信念です。一貫性は非常に重要だと思います。複数の場所で複数の方法で作業を行うと、新しいバグのリスクが高まります。個人的には、デフォルトのオブジェクトアプローチを採用しました。これは、ドメインモデルで使用するエッセンスパターンでうまく機能するためです。すべてのドメインオブジェクトに対してテストして、データが設定されているかどうかを確認できる単一の汎用拡張メソッドがあるので、objectname.IsDefault()の呼び出しでDOをテストできることを確認します-等価チェックを直接回避します。
ジョセフフェリス

3

ビジネスオブジェクトには、2つの主要なGetメソッドがあります。

コンテキスト内で物事をシンプルに保つには、次のようにします。

// Returns null if user does not exist
public UserEntity GetUserById(Guid userId)
{
}

// Returns a New User if user does not exist
public UserEntity GetNewOrExistingUserById(Guid userId)
{
}

最初の方法は特定のエンティティを取得するときに使用され、2番目の方法は特にWebページでエンティティを追加または編集するときに使用されます。

これにより、使用されている状況で両方の長所を活用できます。


3

私はフランスのIT学生なので、英語が下手です。私たちのクラスでは、そのようなメソッドは決してnullや空のオブジェクトを返すべきではないと言われています。このメソッドのユーザーは、探しているオブジェクトが存在することを確認してから、それを取得しようとします。

Javaを使用してassert exists(object) : "You shouldn't try to access an object that doesn't exist";、「前提条件」を表すために、nullを返す可能性のあるメソッドの最初にaを追加するように求められます(英語の単語が何であるかわかりません)。

IMOこれは本当に使いやすいものではありませんが、それが私が使用しているものであり、より良いものを待っています。


1
回答ありがとうございます。しかし、それが存在するかどうかを最初にチェックするという考えは嫌いです。その理由は、データベースに追加のクエリを生成するためです。数百万人が1日にアクセスするアプリケーションでは、パフォーマンスが大幅に低下する可能性があります。
7wp、2009年

1
1つの利点は、存在のチェックが適切に抽象的であることです。if(userExists)がわずかに読みやすく、問題の領域に近く、「コンピューター」が少ない場合:if(user == null)
timoxley

そして、「もし(x == null)」は何十年も前のパターンであり、これまでに見たことがないなら、コードを非常に長く書いていない(そして、それに慣れているはずだ)と私は主張します数百万行のコード)。「コンピューター」?データベースアクセスについて話している...
ロイドサージェント2014

3

ユーザーが見つからないというケースが頻繁に発生し、状況に応じてさまざまな方法でそれを処理したい場合(例外をスローしたり、空のユーザーに置き換えたりすることがある)、F#OptionまたはHaskellのMaybeタイプに近いものを使用することもできます、「値がない」ケースを「何かが見つかりました!」から明示的に分離します。データベースアクセスコードは次のようになります。

public Option<UserEntity> GetUserById(Guid userId)
{
 //Imagine some code here to access database.....

 //Check if data was returned and return a null if none found
 if (!DataExists)
    return Option<UserEntity>.Nothing; 
 else
    return Option.Just(existingUserEntity);
}

そして、このように使用されます:

Option<UserEntity> result = GetUserById(...);
if (result.IsNothing()) {
    // deal with it
} else {
    UserEntity value = result.GetValue();
}

残念ながら、誰もがこのようなタイプを独自に転がしているようです。


2

通常はnullを返します。例外をスローせず、あらゆる場所で大量のtry / catchを使用せずに何かが台無しになったかどうかを検出するための迅速かつ簡単なメカニズムを提供します。


2

コレクション型の場合は空のコレクションを返します。他のすべての型の場合は、NullObjectパターンを使用して、戻り値の型と同じインターフェイスを実装するオブジェクトを返します。パターンの詳細については、リンクテキストを確認してください

NullObjectパターンを使用すると、次のようになります。

public UserEntity GetUserById(Guid userId)

{//データベースにアクセスするためのコードを想像してください...

 //Check if data was returned and return a null if none found
 if (!DataExists)
    return new NullUserEntity(); //Should I be doing this here instead? return new UserEntity();  
 else
    return existingUserEntity;

}

class NullUserEntity: IUserEntity { public string getFirstName(){ return ""; } ...} 

2

他の人が言ったことをより忠実に表現するには...

例外は例外的な状況用です

このメソッドが純粋なデータアクセスレイヤーである場合、selectステートメントに含まれるパラメーターを指定すると、オブジェクトを作成するための行が見つからない可能性があるため、nullを返すことはこのように許容されますデータアクセスロジックです。

一方、パラメーターが主キーを反映することを期待していて、1行しか返さない場合、複数の行を取得すると、例外がスローされます。0はnullを返しても問題ありません、2はそうではありません。

ここで、LDAPプロバイダーに対してチェックしてから詳細を取得するためにDBに対してチェックするログインコードがあり、それらが常に同期しているはずである場合、例外を投げる可能性があります。他の人が言ったように、それはビジネスルールです。

これが一般的なルールだと言います。あなたはそれを壊したいかもしれない時があります。ただし、C#(その多く)とJava(その少し)を使用した私の経験と実験から、条件付きロジックを介して予測可能な問題を処理するよりも、例外を処理する方がはるかにコストのかかるパフォーマンスであることがわかりました。場合によっては、2桁または3桁高い曲に話をしていることがあります。したがって、コードがループになる可能性がある場合は、nullを返してテストすることをお勧めします。


2

私の疑似php /コードを許してください。

結果の使用目的に依存すると思います。

戻り値を編集/変更して保存する場合は、空のオブジェクトを返します。このように、同じ関数を使用して、新規または既存のオブジェクトにデータを入力できます。

主キーとデータの配列を取り、行にデータを入力し、結果のレコードをデータベースに保存する関数があるとします。いずれかの方法でオブジェクトにデータを入力するつもりなので、ゲッターから空のオブジェクトを取得することは非常に有利です。そうすれば、どちらの場合でも同じ操作を実行できます。ゲッター関数の結果は何があっても使用します。

例:

function saveTheRow($prim_key, $data) {
    $row = getRowByPrimKey($prim_key);

    // Populate the data here

    $row->save();
}

ここで、同じ一連の操作がこのタイプのすべてのレコードを操作することがわかります。

ただし、戻り値の最終的な目的がデータを読み取って何かを実行することである場合は、nullを返します。このようにして、データが返されなかったかどうかを非常に迅速に判断し、適切なメッセージをユーザーに表示できます。

通常、データを取得する関数で例外をキャッチし(エラーメッセージなどをログに記録できるようにします)、キャッチから直接nullを返します。通常、エンドユーザーにとって問題は問題ではないので、データを取得する関数にエラーのログ記録/処理を直接カプセル化するのが最善です。大企業で共有コードベースを維持している場合は、最も怠惰なプログラマでも適切なエラーログ/処理を強制できるため、これは特に有益です。

例:

function displayData($row_id) {
    // Logging of the error would happen in this function
    $row = getRow($row_id);
    if($row === null) {
        // Handle the error here
    }

    // Do stuff here with data
}

function getRow($row_id) {
 $row = null;
 try{
     if(!$db->connected()) {
   throw excpetion("Couldn't Connect");
  }

  $result = $db->query($some_query_using_row_id);

  if(count($result) == 0 ) {
   throw new exception("Couldn't find a record!");
  }

  $row = $db->nextRow();

 } catch (db_exception) {
  //Log db conn error, alert admin, etc...
  return null; // This way I know that null means an error occurred
 }
 return $row;
}

それが私の原則です。これまでのところうまくいきました。


2

興味深い質問です。コードの責任に常に依存するため、「正しい」答えはないと思います。あなたの方法は、見つかったデータが問題ではないかどうかを知っていますか?ほとんどの場合、答えは「いいえ」です。そのため、nullを返し、発信者に状況を処理させるのは完璧です。

スローメソッドとnullを返すメソッドを区別するための良いアプローチは、チーム内の規則を見つけることかもしれません。nullを返す可能性のあるメソッドは、別の方法で名前を付けることができます。代わりに「検索...」を使用できます。


+1その関数がどのように消費されるかをプログラマーに知らせるために、統一された命名規則を使用するというアイデアが好きです。
7wp、2009年

1
突然、これがLINQの機能であることを認識しました。First(...)とFirstOrDefault(...)の比較
Marc Wittke

2

返されたオブジェクトが反復可能なものである場合、空のオブジェクトを返すため、最初にnullをテストする必要はありません。

例:

bool IsAdministrator(User user)
{
    var groupsOfUser = GetGroupsOfUser(user);

    // This foreach would cause a run time exception if groupsOfUser is null.
    foreach (var groupOfUser in groupsOfUser) 
    {
        if (groupOfUser.Name == "Administrators")
        {
            return true;
        }
    }

    return false;
}

2

どのメソッドからもnullを返さないで、代わりにOption機能タイプを使用するのが好きです。結果を返せないメソッドは、nullではなく空のOptionを返します。

また、結果を返さない可能性のあるメソッドは、名前でそれを示す必要があります。私は通常、メソッドの名前の先頭にTry、TryGet、またはTryFindを配置して、空の結果(TryFindCustomer、TryLoadFileなど)を返す可能性があることを示します。

これにより、呼び出し元は結果にコレクションパイプライン処理(Martin Fowlerのコレクションパイプラインを参照)などのさまざまな手法を適用できます。

nullの代わりにOptionを返すことを使用してコードの複雑さを軽減する別の例を次に示します。循環的複雑度を軽減する方法:Option機能タイプ


1
私は答えを書きました、私が上にスクロールしたときにそれがあなたのものに似ているのを見ることができます、そして私は同意します、あなたは0または1要素を持つジェネリックコレクションでオプションタイプを実装できます。追加のリンクをありがとう。
Gabriel P.

1

より多くの肉を磨く:私のDALがGetPersonByIDに対してNULLを返すとしましょう。NULLを受け取った場合、(かなり細い)BLLはどうすればよいですか?そのNULLを渡して、エンドユーザーにそれを心配させます(この場合は、ASP.Netページ)?BLLに例外をスローさせるのはどうですか?

BLLはASP.NetやWin App、または別のクラスライブラリで使用されている可能性があります-エンドコンシューマーがメソッドGetPersonByIDがnullを返すことを本質的に「知っている」と期待するのは不公平だと思います(null型が使用されない限り) )。

私の見解(価値がある)は、何も見つからない場合、DALはNULLを返すということです。一部のオブジェクトの場合は問題ありません。0:多数のリストがある可能性があるため、何もないことは問題ありません(たとえば、お気に入りの本のリスト)。この場合、私のBLLは空のリストを返します。ほとんどの単一のエンティティ(たとえば、ユーザー、アカウント、請求書)がない場合、それは間違いなく問題であり、コストのかかる例外がスローされます。ただし、以前にアプリケーションによって指定された一意の識別子によってユーザーを取得すると見なすと、常にユーザーが返されます。この例外は、例外である「適切な」例外です。BLLのエンドコンシューマー(ASP.Net、f'rinstance)は、物事がおかしいと予想するだけなので、GetPersonByIDへのすべての呼び出しをtry-catchブロックでラップする代わりに、Unhandled Exception Handlerが使用されます。

私のアプローチに明白な問題がある場合、私は常に学びたいと思っていますので、私に知らせてください。他のポスターが言ったように、例外はコストがかかるものであり、「最初にチェックする」アプローチは良いですが、例外はそれだけであるべきです-例外的です。

私はこの投稿を楽しんでいます。「依存する」シナリオに対する多くの良い提案があります:-)


そしてもちろん、今日、私は自分のBLLからNULLを返すシナリオに遭遇しました;-)とはいえ、例外をスローし、消費クラスでtry / catchを使用することはできますが、まだ問題があります。 :私の消費クラスは、try / catchを使用することをどのように知っていますか?同様に、NULLをチェックすることを知っていますか?
マイクキングスコット

@throws doctagを介してメソッドが例外をスローすることをドキュメント化でき、@ return doctagでnullを返す可能性があることをドキュメント化します。
timoxley

1

コードベースの健全性のため、関数はnullを返すべきではないと思います。私はいくつかの理由を考えることができます:

null参照を扱う大量のガード句がありますif (f() != null)

とは何ですかnull、それは受け入れられた回答または問題ですか?nullは特定のオブジェクトの有効な状態ですか?(あなたがコードのクライアントであると想像してください)。すべての参照型をnullにすることができますが、それらは必要ですか?

nullあなたのコードベースの成長に合わせてぶらぶらすることは、ほとんどの場合、時々からいくつかの予期しないNullRefの例外を提供します。

いくつかのソリューション、tester-doer patternまたはoption typefrom関数型プログラミングの実装があります。


0

「IsItThere()」メソッドと「GetItForMe()」メソッドの2つのメソッドが必要であり、これにより競合状態が発生するという回答の数(Web全体)に困惑しています。1つのテストでnullを返し、それを変数に割り当て、変数のnullをチェックする関数の何が問題になっていますか?私の以前のCコードは

if(NULL!=(variable = function(arguments ...))){

したがって、変数の値(またはnull)を取得し、結果を一度に取得します。このイディオムは忘れられましたか?どうして?


0

ここにあるほとんどの投稿に同意しnullます。

私の考えでは、null不可のプロパティを持つ空のオブジェクトを生成するとバグが発生する可能性があります。たとえば、int IDプロパティを持つエンティティの初期値はでID = 0、これは完全に有効な値です。そのオブジェクトが、ある状況下でデータベースに保存された場合、それは悪いことです。

イテレータがあるものについては、常に空のコレクションを使用します。何かのようなもの

foreach (var eachValue in collection ?? new List<Type>(0))

私の意見ではコードのにおいです。コレクションのプロパティをnullにすることはできません。

エッジケースですString。多くの人は、これString.IsNullOrEmptyは本当に必要ではないと言っていますが、空の文字列とnullを常に区別できるわけではありません。さらに、一部のデータベースシステム(Oracle)はそれらをまったく区別しません(''として保存されますDBNULL)。したがって、それらを同等に処理する必要があります。どちらもテキストボックスにも、ほとんどの交換フォーマットが異なるため表現を持っていながら、その理由は、ほとんどの文字列値は、いずれか、ユーザの入力からまたは外部システムから来ている''null。したがって、ユーザーが値を削除したい場合でも、入力コントロールをクリアする以外に何もできません。また、nvarcharDBMSがOracleでない場合、NULL可能とNULL不可のデータベースフィールドの区別は疑わしい以上のものです。''奇妙ですが、UIがこれを許可しないため、制約がマップされません。ですから、ここでの答えは、私の意見では、常に同じように扱うことです。

例外とパフォーマンスに関する質問について:プログラムロジックでは完全に処理できない例外をスローする場合、プログラムが実行していることをいつか中止し、ユーザーに、実行したことをやり直すように依頼する必要があります。その場合、aのパフォーマンス上のペナルティは、catch実際に心配する必要がほとんどありません-ユーザーに部屋の象を尋ねる必要があります(つまり、UI全体を再レンダリングするか、インターネット経由でHTMLを送信します)。したがって、「例外を伴うプログラムフロー」のアンチパターンに従っていない場合は、気にせず、意味があれば1つだけスローしてください。「検証例外」などの境界線の場合でも、ユーザーに再度問い合わせる必要があるため、パフォーマンスは実際には問題になりません。


0

非同期TryGetパターン:

同期メソッドの場合、@ Johann Gerellの 答えはすべての場合に使用するパターンであると私は信じています。

ただし、outパラメーターを指定したTryGetパターンは、非同期メソッドでは機能しません。

C#7のタプルリテラルを使用すると、次のことができます。

async Task<(bool success, SomeObject o)> TryGetSomeObjectByIdAsync(Int32 id)
{
    if (InternalIdExists(id))
    {
        o = await InternalGetSomeObjectAsync(id);

        return (true, o);
    }
    else
    {
        return (false, default(SomeObject));
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.