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如果失败,它会抛出一个。我已经更新了上面的代码来捕获那个异常而不是异常。

编辑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 回答