7

これがC#でのgotoの明確な使用と見なされるかどうか疑問に思っています:

IDatabase database = null;

LoadDatabase:
try
{
    database = databaseLoader.LoadDatabase();
}
catch(DatabaseLoaderException e)
{
    var connector = _userInteractor.GetDatabaseConnector();
    if(connector == null)
        throw new ConfigException("Could not load the database specified in your config file.");
    databaseLoader = DatabaseLoaderFacade.GetDatabaseLoader(connector);
    goto LoadDatabase;
}

スニペットは小さく、意味があるはずなので、これは問題ないと思います。例外を処理した後に操作を再試行したいときに、人々が通常このようなエラーから回復する別の方法はありますか?

編集:それは速かった。いくつかの質問に答えて、物事を少し明確にするために-これは本質的に別の種類のプロジェクトから変換しているプロセスの一部です。_userInteractor.GetDatabaseConnector()呼び出しは、ユーザーが再試行するかどうかを決定する部分です(おそらく、ロード元の構成にあるデータベースとは異なるデータベースを使用します)。nullが返された場合、新しいデータベース接続は指定されておらず、操作は完全に失敗するはずです。

whileループを使用することを考えなかった理由がわかりません。午後5時に近づきすぎているに違いありません。

編集2: LoadDatabase()メソッドを確認しましたが、DatabaseLoaderException失敗するとaがスローされます。上記のコードを更新して、Exceptionではなくその例外をキャッチしました。

編集3:一般的なコンセンサスはそれであるようです

  • ここでgotoを使用する必要はありません。whileループで問題ありません。
  • このような例外を使用することはお勧めできません。ただし、何に置き換えるかはわかりません。
4

7 に答える 7

15

例外を処理した後に操作を再試行したいときに、人々が通常このようなエラーから回復する別の方法はありますか?

はい、呼び出しコードで。このメソッドの呼び出し元に、ロジックを再試行する必要があるかどうかを判断させます。

アップデート:

明確にするために、実際に処理できる場合にのみ例外をキャッチする必要があります。あなたのコードは基本的に次のように言っています:

「何が起こったのかわかりませんが、何をしたとしてもすべてが爆発しました...だからもう一度やりましょう。」

回復できる特定のエラーをキャッチし、残りを次のレイヤーにバブルして処理します。一番上まで到達する例外は、その時点での真のバグを表しています。

更新2:

さて、コメントを介してかなり長い議論を続けるのではなく、半擬似コードの例で詳しく説明します。

一般的な考え方は、テストを実行するためにコードを再構築するだけでよく、ユーザーエクスペリエンスを少し良く処理する必要があるということです。

//The main thread might look something like this

try{
    var database = LoadDatabaseFromUserInput();

    //Do other stuff with database
}
catch(Exception ex){
    //Since this is probably the highest layer,
    // then we have no clue what just happened
    Logger.Critical(ex);
    DisplayTheIHaveNoIdeaWhatJustHappenedAndAmGoingToCrashNowMessageToTheUser(ex);
}

//And here is the implementation

public IDatabase LoadDatabaseFromUserInput(){

    IDatabase database = null;
    userHasGivenUpAndQuit = false;

    //Do looping close to the control (in this case the user)
    do{
        try{
            //Wait for user input
            GetUserInput();

            //Check user input for validity
            CheckConfigFile();
            CheckDatabaseConnection();

            //This line shouldn't fail, but if it does we are
            // going to let it bubble up to the next layer because
            // we don't know what just happened
            database = LoadDatabaseFromSettings();
        }
        catch(ConfigFileException ex){
            Logger.Warning(ex);
            DisplayUserFriendlyMessage(ex);
        }
        catch(CouldNotConnectToDatabaseException ex){
            Logger.Warning(ex);
            DisplayUserFriendlyMessage(ex);
        }
        finally{
            //Clean up any resources here
        }
    }while(database != null); 
}

明らかに、アプリケーションが何をしようとしているのかわかりません。これは、実稼働の例ではないことは間違いありません。うまくいけば、あなたは一般的な考えを得るでしょう。プログラムを再構築して、アプリケーションフローの不要な中断を回避できるようにします。

乾杯、ジョシュ

于 2009-11-19T03:30:01.790 に答える
7

何か足りないのかもしれませんが、while ループを使えないのはなぜですか? コードが提供する例外(悪いコード)機能がある場合、これは同じループを永遠に与えます。

IDatabase database = null;

while(database == null){
   try
   {
        database = databaseLoader.LoadDatabase();
   }
   catch(Exception e)
   {
        var connector = _userInteractor.GetDatabaseConnector();
        if(connector == null)
                throw new ConfigException("Could not load the database specified in your config file.");
        databaseLoader = DatabaseLoaderFacade.GetDatabaseLoader(connector);
        //just in case??
        database = null;
   }
 }

通常のコードで goto を使用する必要がある場合は、論理フローがありません。if、while、for などの標準構造を使用して取得できます。

于 2009-11-19T03:37:43.693 に答える
4

個人的には、これを別のメソッドに入れて、成功または失敗のステータスコードを返します。次に、このメソッドを呼び出すコードで、ステータスコードが「成功」になるまでこれを試行し続ける魔法の回数を持つことができます。制御フローにtry/catchを使用するのは好きではありません。

于 2009-11-19T03:29:45.083 に答える
2

それは明らかですか?あまり。実際にやりたいことは、最初にデータベースをロードしてみて、それがうまくいかない場合は別の方法でロードすることだと思います。そうですか?そのようにコードを書きましょう。

IDatabase loadedDatabase = null;

// first try
try
{
    loadedDatabase = databaseLoader.LoadDatabase();
}
catch(Exception e) { }  // THIS IS BAD DON'T DO THIS

// second try
if(loadedDatabase == null) 
{
    var connector = _userInteractor.GetDatabaseConnector();
    if(connector == null)
        throw new ConfigException("Could not load the database specified in your config file.");
    databaseLoader = DatabaseLoaderFacade.GetDatabaseLoader(connector);
    loadedDatabase = databaseLoader.LoadDatabase()
}

これは、実際に何をしているかをより明確に示しています。追加のボーナスとして、他のプログラマーがあなたの目を抉ることはありません。:)

注: ほとんどの場合、例外をキャッチしたくありません。むしろキャッチしたい、より具体的な例外がある可能性があります。これにより、TheComputerIsOnFireException もキャッチされるため、再試行する価値はありません。

于 2009-11-19T03:44:19.100 に答える
1

いいえ、大丈夫ではありません:http: //xkcd.com/292/

于 2009-11-19T03:30:45.953 に答える
1

余談ですが、常に例外が発生すると、無限ループになる可能性があると思います。

技術的には、goto 構造に問題はありませんが、私にとっては、代わりに while ループを使用することを選択します。何かのようなもの:

IDatabase database = null;

bool bSuccess = false;
int iTries = 0
while (!bSuccess) // or while (database == null)
{
    try
    {
        iTries++;
        database = databaseLoader.LoadDatabase();
        bSuccess = true;
    }
    catch(DatabaseLoaderException e)
    {
        //Avoid an endless loop
        if (iTries > 10)
             throw e;

        var connector = _userInteractor.GetDatabaseConnector();
        if(connector == null)
             throw new ConfigException("Could not load the database specified in your config file.");
        databaseLoader = DatabaseLoaderFacade.GetDatabaseLoader(connector);
    }
}
于 2009-11-19T05:02:37.507 に答える
1

必須の XKCD

于 2009-11-19T13:23:31.553 に答える