2009-11-19 8 views
7

只是想知道,如果這被認爲是在C#中明確使用goto:這是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:普遍的共識似乎是,

  • 使用轉到這裏是沒有必要的 - while循環會做得很好。
  • 使用像這樣的異常不是一個好主意 - 我不知道用什麼來代替它。
+7

沒有必要否決一個合理的問題,因爲它有它的話跳轉。 – 2009-11-19 03:33:05

+1

到OP:轉到使用不需要「清除」。它需要「正確」。你的使用很清楚,但是非常不正確。你在這裏有很好的答案,聽取他們的意見.-) – 2009-11-19 03:44:27

+2

但是,這不男人,這個問題應該downvoted。 – 2009-11-19 03:52:54

回答

15

有另一種方式,人們通常 這樣從錯誤中恢復,當你想 異常處理後重試操作?

是的,在調用代碼中。讓這個方法的調用者決定他們是否需要重試邏輯。

UPDATE:

爲了澄清,你應該只捕獲異常,如果你能真正處理它們。您的代碼基本上說:

「我不知道發生了什麼,但無論我造成的一切 吹起來......讓我們再次做到這一點。」

捕獲您可以從中恢復的特定錯誤,並讓其餘的泡沫直到下一層進行處理。任何異常情況都會導致上面的錯誤代表真正的錯誤。

更新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); 
} 

現在顯然我不知道你的應用程序試圖做什麼,這當然不是一個生產的例子。希望你得到一般想法。重構程序,以避免應用程序流程中出現不必要的中斷。

乾杯, 喬希

+0

這將是我的常規方法,但這是一個批處理過程的一部分,所以我不能只是拋出並期望重試。它從一個配置文件中檢索到的數據庫連接器,錯誤處理使用戶有機會從該配置中的錯誤中恢復。我用'catch(DatabaseLoaderException e)'替換了'catch(Exception e)',以使它更具體。 – 2009-11-19 03:57:01

+0

@Jamie:你也不能抓住,重試,並立即期待成功的結果 - 如果發生異常,循環可能是無止境的。記錄錯誤,甚至通過電子郵件發送。如果你只是拋出錯誤並放棄,下次批處理運行時它仍然會重試,但是稍後 - 稍後有更多機會(有時間解決運行之間的錯誤)而不是連續的循環出錯。 – 2009-11-19 04:04:20

+0

看看我上面的編輯,我提到了這個。一旦用戶停止輸入新的數據庫連接信息,循環將結束 - >他們可以在任何階段取消它。 _userInteractor是UI的接口,如果用戶取消,則返回null。所以這不會是一個連續的錯誤。 – 2009-11-19 04:09:28

4

就我個人而言,我會在一個單獨的方法中返回一個成功或失敗的狀態代碼。然後,在調用這個方法的代碼中,我可以有一些奇妙的次數,我會繼續嘗試,直到狀態代碼是「Success」。我只是不喜歡使用try/catch來控制流量。

+0

神奇的次數是至關重要的:如果錯誤依然存在,那麼OP的代碼將會進行無限循環(如果發生錯誤,這可能很可能)。 – 2009-11-19 03:32:47

+0

+1非常「乾淨」的解決方案。 – PRR 2009-11-19 07:11:48

+0

您可以將支票包裹在if語句周圍的幻數上,如果您的幻數是0或-1,那麼在您獲得成功之前,不要打擾停止循環。 – 2009-11-19 13:15:57

1

不,這是不行的:http://xkcd.com/292/

+1

關於gotos的一攬子聲明與不加區分地使用它們一樣糟糕。 Goto是一種高級編程結構,僅供高級程序員使用,以簡化方法結構。 OP的使用是不恰當的,這是肯定的(已經回答了爲什麼已經),但是然後說gotos總是不好也是不好的。 – 2009-11-19 03:43:10

+2

嗯...... Argumentum ad XKCD是一個可以接受的優秀謬論! – MPelletier 2009-11-19 03:46:07

+0

很有趣,但實際上並沒有幫助。 -1 – Oorang 2009-11-19 04:39:21

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,那麼缺少邏輯流程。你可以使用標準結構,如果,同時,等等。

+0

我不應該有upvoted,這可以永遠循環。 – MPelletier 2009-11-19 04:15:48

+0

它不會,因爲它不會永久加載相同的數據庫。 – 2009-11-19 04:25:07

+0

如果LoadDatabase()返回null並且不失敗,它會不會? – MPelletier 2009-11-19 04:32:06

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,之後它不值得重試。

+0

啊,我現在看到你想每次失敗都要求用戶提供不同的數據庫連接,所以這是行不通的。但它應該更好地回答你的問題:不,你的構建與goto不清楚。 :) 正如其他人所指出的那樣,一個while循環更合適。 – 2009-11-19 03:46:57

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); 
    } 
}