2008-08-04 71 views
32

Tôi chưa bao giờ hoàn toàn hài lòng với cách xử lý ngoại lệ, có rất nhiều ngoại lệ và try/catch mang đến bảng (stack unwinding, vv), nhưng dường như phá vỡ rất nhiều mô hình OO trong quá trình .Giảm mã xử lý lỗi trùng lặp trong C#?

Dù sao, đây là vấn đề:

Hãy nói rằng bạn có một số lớp bao bọc hoặc bao gồm tập tin mạng hoạt động IO (ví dụ: đọc và viết một số tập tin tại một số đường dẫn UNC đặc biệt nơi nào đó). Vì nhiều lý do bạn không muốn những hoạt động IO thất bại, vì vậy nếu bạn phát hiện ra rằng chúng không thành công, bạn thử lại chúng và bạn tiếp tục thử lại cho đến khi chúng thành công hoặc bạn đạt đến thời gian chờ. Tôi đã có một lớp RetryTimer tiện lợi mà tôi có thể khởi tạo và sử dụng để ngủ chủ đề hiện tại giữa các lần thử lại và xác định khi nào hết thời gian chờ, v.v.

Vấn đề là bạn có một loạt hoạt động IO theo một số phương pháp lớp này, và bạn cần phải quấn từng lớp trong logic thử try-catch/retry.

Dưới đây là một ví dụ đoạn mã:

RetryTimer fileIORetryTimer = new RetryTimer(TimeSpan.FromHours(10)); 
bool success = false; 
while (!success) 
{ 
    try 
    { 
     // do some file IO which may succeed or fail 
     success = true; 
    } 
    catch (IOException e) 
    { 
     if (fileIORetryTimer.HasExceededRetryTimeout) 
     { 
      throw e; 
     } 
     fileIORetryTimer.SleepUntilNextRetry(); 
    } 
} 

Vì vậy, làm thế nào để bạn tránh sao chép hầu hết các mã này cho mỗi tập tin hoạt động IO trong suốt lớp học? Giải pháp của tôi là sử dụng các khối đại biểu ẩn danh và một phương thức duy nhất trong lớp đã thực thi khối ủy nhiệm được truyền cho nó. Điều này cho phép tôi thực hiện những việc như thế này theo các phương pháp khác:

this.RetryFileIO(delegate() 
    { 
     // some code block 
    }); 

Tôi thích điều này một chút, nhưng rất nhiều điều mong muốn. Tôi muốn nghe người khác giải quyết vấn đề như thế nào.

+1

Chỉ là một FYI chung: Đó là [gần như luôn luôn * tốt hơn] (http://philosopherdeveloper.wordpress.com/2010/05/05/re-throwing-caught-exceptions/) chỉ đơn giản là 'ném; 'thay vào đó của 'throw e; ' –

Trả lời

13

Đây có vẻ là cơ hội tuyệt vời để xem qua Lập trình hướng đến khía cạnh. Đây là một bài viết hay trên AOP in .NET. Ý tưởng chung là bạn sẽ trích xuất mối quan tâm chéo chức năng (tức là Thử lại trong x giờ) vào một lớp riêng biệt và sau đó bạn sẽ chú thích bất kỳ phương thức nào cần sửa đổi hành vi của chúng theo cách đó. Dưới đây là làm thế nào nó có thể trông (với một phương pháp mở rộng thoải mái trên Int32)

[RetryFor(10.Hours())] 
public void DeleteArchive() 
{ 
    //.. code to just delete the archive 
} 
4

Chỉ cần tự hỏi, bạn cảm thấy phương pháp của bạn để được mong muốn là gì? Bạn có thể thay thế các đại biểu vô danh với một .. tên? đại biểu, giống như

public delegate void IoOperation(params string[] parameters); 

    public void FileDeleteOperation(params string[] fileName) 
    { 
     File.Delete(fileName[0]); 
    } 

    public void FileCopyOperation(params string[] fileNames) 
    { 
     File.Copy(fileNames[0], fileNames[1]); 
    } 

    public void RetryFileIO(IoOperation operation, params string[] parameters) 
    { 
     RetryTimer fileIORetryTimer = new RetryTimer(TimeSpan.FromHours(10)); 
     bool success = false; 
     while (!success) 
     { 
      try 
      { 
       operation(parameters); 
       success = true; 
      } 
      catch (IOException e) 
      { 
       if (fileIORetryTimer.HasExceededRetryTimeout) 
       { 
        throw; 
       } 
       fileIORetryTimer.SleepUntilNextRetry(); 
      } 
     } 
    } 

    public void Foo() 
    { 
     this.RetryFileIO(FileDeleteOperation, "L:\file.to.delete"); 
     this.RetryFileIO(FileCopyOperation, "L:\file.to.copy.source", "L:\file.to.copy.destination"); 
    } 
2

Bạn cũng có thể sử dụng một cách tiếp cận OO hơn:

  • Tạo một lớp cơ sở mà không được xử lý lỗi và kêu gọi một phương pháp trừu tượng để thực hiện công việc cụ thể. (Mẫu phương thức mẫu)
  • Tạo các lớp cụ thể cho mỗi thao tác.

Điều này có lợi thế là đặt tên cho từng loại thao tác bạn thực hiện và cung cấp cho bạn mẫu Command - các thao tác đã được biểu diễn dưới dạng đối tượng.

2

Đây là những gì tôi đã làm gần đây. Nó có thể đã được thực hiện ở nơi khác tốt hơn, nhưng nó có vẻ khá sạch sẽ và tái sử dụng.

Tôi có một phương pháp hữu ích mà trông như thế này:

public delegate void WorkMethod(); 

    static public void DoAndRetry(WorkMethod wm, int maxRetries) 
    { 
     int curRetries = 0; 
     do 
     { 
      try 
      { 
       wm.Invoke(); 
       return; 
      } 
      catch (Exception e) 
      { 
       curRetries++; 
       if (curRetries > maxRetries) 
       { 
        throw new Exception("Maximum retries reached", e); 
       } 
      } 
     } while (true); 
    } 

Sau đó, trong ứng dụng của tôi, tôi sử dụng Lamda cú pháp biểu hiện C# 's để giữ cho mọi thứ gọn gàng:

Utility.DoAndRetry(() => ie.GoTo(url), 5); 

Điều này đòi hỏi phương pháp và thử lại tôi lên đến 5 lần. Tại lần thử thứ năm, ngoại lệ ban đầu được trả về bên trong một ngoại lệ thử lại.

+0

Nhưng tại sao đại biểu' WorkMethod' tùy chỉnh thay vì 'Action'? –

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