12

例外処理には 2 つのアプローチがあることを知っています。それらを見てみましょう。

  1. 契約アプローチ。

    メソッドがメソッド ヘッダーで実行すると述べていることを実行しない場合、例外がスローされます。したがって、メソッドは操作を実行することを「約束」し、何らかの理由で失敗した場合は例外をスローします。

  2. 例外的なアプローチ。

    本当に奇妙なことが起こった場合にのみ、例外をスローします。通常の制御フロー (If ステートメント) で状況を解決できる場合は、例外を使用しないでください。コントラクト アプローチの場合のように、制御フローに例外を使用しません。

さまざまなケースで両方のアプローチを使用してみましょう。

OrderProduct というメソッドを持つ Customer クラスがあります。

契約アプローチ:

class Customer
{
     public void OrderProduct(Product product)
     {
           if((m_credit - product.Price) < 0)
                  throw new NoCreditException("Not enough credit!");
           // do stuff 
     }
}

例外的なアプローチ:

class Customer
{
     public bool OrderProduct(Product product)
     {
          if((m_credit - product.Price) < 0)
                   return false;
          // do stuff
          return true;
     }
}

if !(customer.OrderProduct(product))
            Console.WriteLine("Not enough credit!");
else
   // go on with your life

ここでは例外的なアプローチを好みます。なぜなら、顧客が宝くじに当選しなかったと仮定してお金を持っていないということは、真に例外的ではないからです。

しかし、これは私が契約スタイルを誤っている状況です。

優れた:

class CarController
{
     // returns null if car creation failed.
     public Car CreateCar(string model)
     {
         // something went wrong, wrong model
         return null;
     }
 }

CreateCar というメソッドを呼び出すと、実行中のコードを数十行後に破壊する可能性のあるお粗末な null ポインターではなく、Car インスタンスが期待されます。したがって、私はこれよりも契約を好みます。

class CarController
{
     
     public Car CreateCar(string model)
     {
         // something went wrong, wrong model
         throw new CarModelNotKnownException("Model unkown");

         return new Car();
     }
 }

あなたはどちらのスタイルを使いますか?例外に対する最も一般的なアプローチは何だと思いますか?

4

5 に答える 5

6

私はあなたが「契約」アプローチと呼ぶものを支持します。例外をサポートする言語では、エラーを示すために null やその他の特別な値を返す必要はありません。「if (result == NULL)」または「if (result == -1)」句が非常に単純で単純なロジックと混同されていない場合、コードを理解するのがはるかに簡単になります。

于 2008-08-25T17:01:07.287 に答える
1

私の通常のアプローチは、コントラクトを使用して、「クライアント」呼び出しによるあらゆる種類のエラー、つまり外部エラー (つまり ArgumentNullException) によるエラーを処理することです。

引数のすべてのエラーは処理されません。例外が発生し、「クライアント」がその処理を担当します。一方、内部エラーの場合は、(何らかの理由でデータベース接続を取得できないかのように) 常にそれらを修正しようとし、それを処理できない場合にのみ、例外を再発生させます。

そのようなレベルで処理されない例外のほとんどはクライアントで処理できないため、おそらく最も一般的な例外ハンドラに移動することに注意することが重要です。

于 2008-08-25T17:00:45.273 に答える
0

外部プログラムで使用される (または他のプログラムで再利用される) クラスを作成する場合は、コントラクト アプローチを使用する必要があります。これの良い例は、あらゆる種類の API です。

于 2008-08-25T16:59:43.280 に答える
0

どちらのアプローチも正しいです。これが意味することは、真に例外的ではないすべてのケースに対して、例外をスローする必要のない動作を指定するような方法でコントラクトを作成する必要があるということです。

コードの呼び出し元が期待していることに基づいて、状況によっては例外である場合とそうでない場合があることに注意してください。ディクショナリに特定の項目が含まれていることを呼び出し元が予期しており、その項目がないことが重大な問題を示している場合、項目が見つからないことは例外的な状態であり、例外がスローされる原因となります。ただし、呼び出し元がアイテムが存在するかどうかを実際には知らず、アイテムの存在または不在を処理する準備が等しく整っている場合、アイテムの不在は予期される条件であり、例外は発生しません。このような呼び出し元の期待値の変動を処理する最善の方法は、コントラクトで DoSomething メソッドと TryDoSomething メソッドの 2 つのメソッドを指定することです。

TValue GetValue(TKey キー);
bool TryGetValue(TKey キー、ref TValue 値);

標準の「試行」パターンは上に示したとおりですが、アイテムを生成するインターフェイスを設計している場合は、いくつかの代替手段も役立つ場合があることに注意してください。

// 失敗した場合は、ok を false に設定し、default<TValue> を返します。
TValue TryGetResult(ref bool ok, TParam param);
// 失敗した場合は、GetKeyErrorInfo で特定の問題を示します
// そして、default<TValue> を返します。
TValue TryGetResult(ref GetKeyErrorInfo errorInfo, ref TParam param);

インターフェイス内で通常の TryGetResult パターンのようなものを使用すると、結果の型に関してインターフェイスが不変になることに注意してください。上記のパターンのいずれかを使用すると、結果の型に関してインターフェースを共変にすることができます。また、結果を「var」宣言で使用できるようになります。

  var myThingResult = myThing.TryGetSomeValue(ref OK, なんでも);
  もし (OK) { do_whatever }

標準的なアプローチではありませんが、場合によっては利点が正当化される場合があります。

于 2011-09-22T15:43:02.030 に答える
0

実際に例外に興味があり、それらを使用して堅牢なシステムを構築する方法を考えたい場合は、 ソフトウェア エラーが発生した場合に信頼できる分散システムを作成する を読むことを検討してください。

于 2008-08-25T18:13:44.443 に答える