2009-11-19 35 views
7

Chỉ cần tự hỏi nếu điều này được coi là sử dụng rõ ràng về goto trong C#:Đây có phải là cách sử dụng rõ ràng của goto không?

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

tôi cảm thấy như thế này là ok, vì đoạn là nhỏ và nên có ý nghĩa. Có cách nào khác mọi người thường phục hồi từ các lỗi như thế này khi bạn muốn thử lại hoạt động sau khi xử lý ngoại lệ?

Chỉnh sửa: Tốc độ nhanh. Để trả lời một số câu hỏi và làm rõ mọi thứ một chút - đây là một phần của quá trình chủ yếu chuyển đổi từ một loại dự án khác. Cuộc gọi _userInteractor.GetDatabaseConnector() là phần sẽ xác định xem người dùng có muốn thử lại (có thể với một cơ sở dữ liệu khác với cơ sở dữ liệu mà họ đang tải). Nếu nó trả về null, thì không có kết nối cơ sở dữ liệu mới nào được chỉ định và thao tác sẽ thất bại hoàn toàn.

Tôi không biết tại sao tôi không nghĩ đến việc sử dụng vòng lặp while. Nó phải nhận được quá gần đến 5 giờ chiều.

Chỉnh sửa 2: Tôi đã xem phương thức LoadDatabase() và nó sẽ ném một DatabaseLoaderException nếu không thành công. Tôi đã cập nhật mã ở trên để bắt ngoại lệ đó thay vì Ngoại lệ.

Sửa 3: Sự đồng thuận chung dường như là

  • Sử dụng goto ở đây là không cần thiết - một vòng lặp while sẽ làm tốt.
  • Sử dụng ngoại lệ như thế này không phải là ý tưởng hay - tôi không chắc chắn nên thay thế nó bằng cách nào.
+7

không cần phải bỏ phiếu xuống một câu hỏi hợp pháp vì nó có goto từ trong đó. –

+1

Để OP: Sử dụng Goto không cần phải "rõ ràng". Nó cần phải là "đúng". Cách sử dụng của bạn rõ ràng, nhưng rất không chính xác. Bạn có câu trả lời tuyệt vời ở đây, chú ý đến họ :-) –

+2

Nhưng đó không phải là nam giới mà câu hỏi nên được giảm bớt. –

Trả lời

15

Có một cách khác người thường phục hồi từ các lỗi như thế này khi bạn muốn thử lại thao tác sau xử lý các ngoại lệ?

Có, trong mã gọi điện. Hãy để người gọi phương thức này quyết định xem họ có cần thử lại logic hay không.

UPDATE:

Để làm rõ, bạn chỉ nên bắt ngoại lệ nếu bạn thực sự có thể xử lý chúng. Mã của bạn về cơ bản nói:

"Tôi không biết điều gì đã xảy ra, nhưng bất cứ điều gì tôi đã làm mọi thứ để nổ tung ... vì vậy hãy làm lại."

Bắt lỗi cụ thể mà bạn có thể khôi phục và để phần còn lại bong bóng lên lớp tiếp theo cần xử lý. Bất kỳ trường hợp ngoại lệ nào làm cho nó lên đến đỉnh đều đại diện cho các lỗi thực sự tại thời điểm đó.

UPDATE 2:

Ok, vậy chứ không phải tiếp tục một cuộc thảo luận khá dài qua các ý kiến ​​tôi sẽ xây dựng với một mã ví dụ bán giả.

Ý tưởng chung là bạn chỉ cần cấu trúc lại mã để thực hiện kiểm tra và xử lý trải nghiệm người dùng tốt hơn một chút.

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

Bây giờ rõ ràng là tôi không biết ứng dụng của bạn đang cố gắng làm gì và đây chắc chắn không phải là ví dụ sản xuất. Hy vọng rằng bạn sẽ có được ý tưởng chung. Tái cấu trúc chương trình để bạn có thể tránh được mọi sự cố không cần thiết trong luồng ứng dụng.

Chúc mừng, Josh

+0

Đây sẽ là cách tiếp cận bình thường của tôi, nhưng đây là một phần của quá trình xử lý hàng loạt vì vậy tôi không thể ném và mong đợi thử lại.Trình kết nối cơ sở dữ liệu mà nó bắt đầu bằng được lấy ra từ một tệp cấu hình, việc xử lý lỗi sẽ cho người dùng cơ hội phục hồi từ một lỗi trong cấu hình đó. Tôi đã thay thế 'catch (Exception e)' bằng 'catch (DatabaseLoaderException e)' để làm cho nó cụ thể hơn. –

+0

@Jamie: Bạn không thể bắt, thử lại và mong đợi kết quả thành công ngay lập tức - vòng lặp có thể rất tốt nếu vô tận xảy ra. Đăng nhập lỗi, thậm chí gửi nó qua email. Nếu bạn chỉ cần ném lỗi và hủy, lần tiếp theo chạy nó sẽ vẫn thử lại, nhưng sau đó - có nhiều khả năng nó sẽ hoạt động sau (có thời gian để giải quyết lỗi giữa các lần chạy) thay vì liên tục lặp lại lỗi. –

+0

Hãy xem bản chỉnh sửa của tôi ở trên, tôi đã đề cập đến điều này. Vòng lặp sẽ kết thúc khi người dùng dừng nhập thông tin kết nối cơ sở dữ liệu mới -> họ có thể hủy bỏ nó ở bất kỳ giai đoạn nào. _userInteractor là một giao diện cho giao diện người dùng, và sẽ trả về null nếu người dùng hủy bỏ. Vì vậy, nó sẽ không phải là một lỗi liên tục. –

4

Cá nhân, tôi sẽ có điều này theo phương pháp riêng trả về mã trạng thái thành công hay thất bại. Sau đó, trong đoạn mã sẽ gọi phương thức này, tôi có thể có một số số lần phép thuật mà tôi sẽ tiếp tục cố gắng cho đến khi mã trạng thái là "Thành công". Tôi chỉ không thích sử dụng try/catch cho luồng điều khiển.

+0

Số ma thuật của thời gian là rất quan trọng: mã OP sẽ thực hiện một vòng lặp vô hạn nếu lỗi vẫn tồn tại (có lẽ rất có thể xảy ra nếu xảy ra lỗi). –

+0

+1 giải pháp "sạch". – PRR

+0

Bạn có thể quấn séc của mình đến số ma thuật xung quanh câu lệnh if và nếu số ma thuật của bạn là 0 hoặc -1, thì đừng bận tâm bao giờ dừng vòng lặp cho đến khi bạn nhận được Thành công. –

1

Không, nó không phải là okay: http://xkcd.com/292/

+1

Phát biểu chăn về gotos cũng tệ như sử dụng chúng một cách bừa bãi. Goto là một cấu trúc lập trình nâng cao chỉ được sử dụng bởi các lập trình viên nâng cao để đơn giản hóa cấu trúc phương thức. Việc sử dụng trong câu hỏi của OP là không thích hợp, đó là chắc chắn (nó đã được trả lời tại sao đã), nhưng sau đó nói gotos luôn luôn xấu là xấu quá. –

+2

Hmmm ... Argumentum ad XKCD là một sai lầm tuyệt vời chấp nhận được và tuyệt vời! – MPelletier

+0

Hài hước, nhưng không thực sự hữu ích. -1 – Oorang

7

lẽ im thiếu một cái gì đó nhưng tại sao không thể bạn chỉ cần sử dụng một vòng lặp while? điều này sẽ cung cấp cho bạn cùng một vòng lặp mãi mãi nếu bạn có một ngoại lệ (đó là mã xấu) chức năng mã của bạn cung cấp.

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

nếu bạn phải sử dụng goto trong mã thông thường, bạn thiếu luồng logic. bạn có thể sử dụng các cấu trúc chuẩn, nếu, trong khi, v.v.

+0

Tôi không nên upvoted, điều này có thể lặp mãi mãi. – MPelletier

+0

Nó sẽ không, bởi vì nó không tải cùng một cơ sở dữ liệu mãi mãi. –

+0

Nếu LoadDatabase() trả về null và không thất bại, nó sẽ, không? – MPelletier

2

Có rõ không? Không hẳn. Những gì bạn thực sự muốn làm, tôi nghĩ, đầu tiên là thử tải cơ sở dữ liệu và sau đó, nếu điều đó không hiệu quả, hãy thử tải nó theo một cách khác. Có đúng không? Hãy viết mã theo cách đó.

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

Điều này minh họa rõ ràng hơn những gì bạn đang thực sự làm. Như một tiền thưởng thêm, các lập trình viên khác sẽ không gouge ra mắt của bạn. :)

LƯU Ý: bạn gần như chắc chắn không muốn bắt ngoại lệ. Có khả năng là một ngoại lệ cụ thể hơn mà bạn thà bị bắt. Điều này cũng sẽ bắt TheComputerIsOnFireException, sau đó nó không thực sự đáng thử lại.

+0

Ah, tôi thấy bây giờ mà bạn muốn tiếp tục yêu cầu người dùng cho một kết nối cơ sở dữ liệu khác nhau mỗi lần nó không thành công, do đó, điều này sẽ không hoạt động. Nhưng nó phải trả lời câu hỏi của bạn tốt hơn: không, cấu trúc của bạn với goto không rõ ràng. :) Một vòng lặp trong khi thích hợp hơn, như những người khác đã lưu ý. –

1

Trên một lưu ý phụ, tôi nghĩ rằng có tiềm năng cho một vòng lặp vô tận nếu bạn luôn nhận được một ngoại lệ.

Về mặt kỹ thuật không có gì sai với cấu trúc goto của bạn, nhưng đối với tôi, tôi sẽ chọn sử dụng vòng lặp while thay thế. Một cái gì đó như:

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); 
    } 
} 
Các vấn đề liên quan