2010-11-10 16 views
7

Có cách nào tốt hơn để viết mã này mà không sử dụng goto không? Nó có vẻ khó xử, nhưng tôi không thể nghĩ ra một cách tốt hơn. Tôi cần để có thể thực hiện một lần thử lại, nhưng tôi không muốn sao chép bất kỳ mã nào.Cách tốt hơn để viết lại logic mà không cần goto

public void Write(string body) 
{ 
    bool retry = false; 
RetryPoint: 
    try 
    { 
     m_Outputfile.Write(body); 
     m_Outputfile.Flush(); 
    } 
    catch (Exception) 
    { 
     if(retry) 
      throw; 
     // try to re-open the file... 
     m_Outputfile = new StreamWriter(m_Filepath, true); 
     retry = true; 
     goto RetryPoint; 
    } 
} 
+10

xin lỗi, không thể cưỡng lại! http://xkcd.com/292/ – Joe

+2

Luôn luôn có một cách tốt hơn để viết logic mà không có một goto. –

+1

@McWafflestix: Tôi không đồng ý. Có một số trường hợp * rất hiếm khi sử dụng 'goto' trên thực tế tạo ra mã sạch hơn - phá vỡ vòng lặp lồng nhau là một ví dụ thường được trích dẫn (vì C# không có dấu ngắt như Java). Xem http://stackoverflow.com/questions/2542289/is-there-ever-a-reason-to-use-goto-in-modern-net-code để biết thêm. – Heinzi

Trả lời

15

Đây là logic cơ bản mà tôi sẽ sử dụng thay vì một lệnh goto:

bool succeeded = false; 
int tries = 2; 

do 
{ 
    try 
    { 
     m_Outputfile = new StreamWriter(m_Filepath, true); 
     m_Outputfile.Write(body); 
     m_Outputfile.Flush(); 
     succeeded = true; 
    } 
    catch(Exception) 
    { 
     tries--; 
    } 
} 
while (!succeeded && tries > 0); 

Tôi chỉ cần thêm # của cố gắng logic, mặc dù câu hỏi ban đầu đã không có bất kỳ.

+0

Điều này thực hiện các lần thử vô hạn, sử dụng cú pháp C++ để nắm bắt và không phục hồi lại. Tôi không có ý tưởng tại sao nó đã được upvoted khi nó rõ ràng sai. –

+0

Ồ, và nó không mở lại do lỗi. –

+2

@Steven Sudit: Phần thân của khối catch được ngụ ý giống với mã gốc. – LBushkin

1

Nếu bạn đặt nó trong vòng lặp thì sao? Một cái gì đó tương tự như thế này, có thể.

while(tryToOpenFile) 
{ 
    try 
    { 
     //some code 
    } 
    catch 
    { 
    } 
    finally 
    { 
     //set tryToOpenFile to false when you need to break 
    } 
} 
+0

Điều này có thử lại vô hạn và thường không giải quyết được sự cố. –

+0

Vâng, đó là lý do tại sao tôi nói * một cái gì đó tương tự *. Ý tưởng là để làm điều đó trong một vòng lặp, mà dường như là ý tưởng chung trong mọi câu trả lời ở đây. – Joe

+0

Phải, sử dụng vòng lặp là khá rõ ràng (mặc dù không phổ biến). Ma quỷ là trong các chi tiết. –

0

Hãy thử một cái gì đó như sau: giải pháp

int tryCount = 0; 
bool succeeded = false; 

while(!succeeded && tryCount<2){ 
    tryCount++; 
    try{ 
     //interesting stuff here that may fail. 

     succeeded=true; 
    } catch { 
    } 
} 
+0

Bool là không cần thiết. Thêm vào thời điểm này, điều này không ném thất bại cuối cùng. –

1
public void Write(string body, bool retryOnError) 
{ 
    try 
    { 
     m_Outputfile.Write(body); 
     m_Outputfile.Flush(); 
    } 
    catch (Exception) 
    { 
     if(!retryOnError) 
      throw; 
     // try to re-open the file... 
     m_Outputfile = new StreamWriter(m_Filepath, true); 
     Write(body, false); 
    } 
} 
+0

Đệ quy? Có thật không? –

+0

@Steven: Chắc chắn, tại sao không? Đó là một sự đệ quy đuôi đơn giản và rất dễ đọc: 'Viết (cơ thể, sai)' khá rõ ràng tài liệu ý định của nhà văn (thử lại một lần nữa nhưng không thử lại) và nó tránh lộn xộn mã (không có vòng lặp, không có biến 'thành công'). Việc thay thế 'retryOnError' bằng số lần thử lại cũng là một bài tập dễ dàng ... – Heinzi

+0

Một vài lý do. Do cách thức hoạt động của GC, đệ quy giữ cho các đối tượng còn sống lâu hơn, vì vậy nó nên tránh khi một giải pháp lặp lại đủ rõ ràng. Khi tối ưu đệ quy đuôi là có thể, điều này là ít của một vấn đề. Ví dụ này đệ quy không quá một lần, nhưng bạn sẽ thay đổi bool thành đếm ngược thử lại nếu bạn muốn tăng (và bạn có thể). Sự thay đổi như vậy sẽ phóng đại các hiệu ứng. –

5

Michael không hoàn toàn đáp ứng các yêu cầu, mà là để thử lại một số cố định của thời gian, ném sự thất bại cuối cùng.

Đối với điều này, tôi muốn giới thiệu một vòng lặp đơn giản, đếm ngược. Nếu bạn thành công, thoát ra với break (hoặc, nếu thuận tiện, trả lại). Nếu không, hãy kiểm tra đánh bắt để xem liệu chỉ mục có giảm xuống 0 hay không. Nếu có, hãy thay đổi lại thay vì ghi nhật ký hoặc bỏ qua.

public void Write(string body, bool retryOnError) 
{ 
    for (int tries = MaxRetries; tries >= 0; tries--) 
    { 
     try 
     { 
      _outputfile.Write(body); 
      _outputfile.Flush(); 
      break; 
     } 
     catch (Exception) 
     { 
      if (tries == 0) 
       throw; 

      _outputfile.Close(); 
      _outputfile = new StreamWriter(_filepath, true); 
     } 
    } 
} 

Trong ví dụ trên, sự trở lại sẽ ổn nhưng tôi muốn hiển thị trường hợp chung.

+1

'if (tries == 0)' –

+0

@Anthony: Cảm ơn bạn, đó là một sai lầm. Tôi sẽ sửa chữa nó ngay lập tức. –

+0

Tôi vừa thực hiện thêm một thay đổi nữa mà tôi nghĩ là có thể áp dụng cho bất kỳ giải pháp nào: Tôi đã tạo nó 'Đóng' nhà biên kịch cũ trước khi mở một chương trình mới. Nếu điều này không được thực hiện, sau đó cái cũ sẽ giữ một xử lý mở cho đến khi GC đá vào, ngăn chặn những cái mới từ làm việc! –

4

@Michael's answer (với khối bắt đầu được triển khai chính xác) có thể dễ sử dụng nhất trong trường hợp của bạn và là đơn giản nhất. Nhưng vì lợi ích của trình bày giải pháp thay thế, đây là một phiên bản mà các yếu tố là "thử" kiểm soát dòng chảy vào một phương pháp riêng biệt:

// define a flow control method that performs an action, with an optional retry 
public static void WithRetry(Action action, Action recovery) 
{ 
    try { 
     action(); 
    } 
    catch (Exception) { 
     recovery(); 
     action(); 
    } 
} 

public void Send(string body) 
{ 
    WithRetry(() => 
    // action logic: 
    { 
     m_Outputfile.Write(body); 
     m_Outputfile.Flush(); 
    }, 
    // retry logic: 
    () => 
    { 
     m_Outputfile = new StreamWriter(m_Filepath, true); 
    }); 
} 

Bạn có thể, tất nhiên, cải thiện điều này với những thứ như đếm thử lại, tuyên truyền lỗi tốt hơn, và vân vân.

+0

Tôi thực sự không chắc chắn về điều này. Có thể cho rằng, giải pháp điều khiển đại biểu này là thanh lịch, linh hoạt và có thể sử dụng lại được. Sau đó, một lần nữa, nó hiện không thực sự làm những gì cần thiết. Cuối cùng, tôi sẽ không bỏ phiếu hay bỏ phiếu. –

+0

@Steven: Tôi có nhớ điều gì đó không? Bằng cách nào nó không đáp ứng được các yêu cầu? – LBushkin

+0

Cách bạn đã đề cập: không có số lần thử lại, không ném vào lần thử cuối cùng, v.v. Không nghi ngờ gì bạn có thể * thực hiện * điều đó là cần thiết - Tôi đã thấy đủ câu trả lời của bạn để chắc chắn - nhưng bạn không có ở thời điểm này. –

0

với một boolean

public void Write(string body) 
{ 
     bool NotFailedOnce = true;    
     while (true) 
     { 
      try 
      { 
       _outputfile.Write(body); 
       _outputfile.Flush();   
       return;      
      } 
      catch (Exception) 
      { 
       NotFailedOnce = !NotFailedOnce; 
       if (NotFailedOnce) 
       { 
        throw; 
       } 
       else 
       { 
        m_Outputfile = new StreamWriter(m_Filepath, true); 
       } 
      } 
     }   
} 
+0

Điều này không thể thực hiện được. Đối với một điều, 'NotFailedOnce =! NotFailedOnce)' sẽ luôn luôn là sai, vì vậy nó sẽ không bao giờ ném. Thay vào đó, nó sẽ lặp mãi mãi. –

+0

@Steve, nhìn lại mã, nó không phải là một sự kiểm tra bình đẳng, mà là một bài tập. – Jimmy

+0

thông minh, có nghĩa là xấu. Trình biên dịch sẽ tạo ra cảnh báo cho điều đó, và nó nên, vì một bài tập ở giữa một vị từ có nhiều khả năng là do lỗi đánh máy. Tương tự như vậy, nó rất thông minh/xấu để thiết lập một bool thành false bằng cách NOT nó thay vì, bạn biết đấy, thiết lập nó thành false. Mã này là không cần thiết obfuscated. –

Các vấn đề liên quan