2012-08-03 27 views
7

Tôi có một tình huống cụ thể mà tôi cần phải bẫy các ngoại lệ và trả lại một đối tượng cho khách hàng thay cho ngoại lệ. Tôi không thể đặt logic xử lý ngoại lệ ở cấp cao hơn tức là quấn Foo trong một mệnh đề thử.Tôi làm cách nào để mã xử lý ngoại lệ tuân theo nguyên tắc DRY?

Tốt nhất là nên chứng minh bằng một số mã mẫu. Logic xử lý ngoại lệ là clouding ý định của phương pháp và nếu tôi có, nhiều phương pháp có ý định tương tự, trong lớp Foo, tôi thấy mình lặp lại hầu hết logic bắt.

Kỹ thuật tốt nhất để bao bọc chức năng ngoại lệ chung trong mã bên dưới là gì?

public class Foo 
{ 
    public Bar SomeMethodThatCanThrowExcepetion() 
    { 
      try 
      { 
       return new Bar().Execute(); 
      } 
      catch(BazException ex) 
      { 
       WriteLogMessage(ex, Bar.ErrorCode); 
       return new Bar() { ErrorMessage = ex.Message, ErrorCode = Bar.ErrorCode;}     
      } 
    } 

    public Baz SomeMethodThatCanThrowExcepetion(SomeObject stuff) 
    { 
      try 
      { 
       return new Baz(stuff).Execute(); 
      } 
      catch(BazException ex) 
      { 
       WriteLogMessage(ex, Baz.ErrorCode); 
       return new Baz() { ErrorMessage = ex.Message, ErrorCode = Baz.ErrorCode;}     
      } 
    } 
} 
+0

Bạn không thể đặt ngoại lệ của mình xử lý một cấp cao hơn và ném ngoại lệ lên chuỗi? – ametren

+0

Thật không may, tôi không thể. Tôi bị giới hạn ở đây. – fin

+0

Có một lớp cơ sở chung giữa 'Bar' và' Baz' không? – Lee

Trả lời

5

cập nhật mỗi comment Lee


Một khả năng là sử dụng một phương pháp helper generic. Một cái gì đó như thế này:

T TryExecute<T>(Func<T> action, int ErrorCode) 
{ 
    try 
    { 
     return action(); 
    } 
    catch (Exception ex) 
    { 
     result = Activator.CreateInstance<T>(); 
     typeof(T).GetProperty("ErrorMessage").SetValue(result, ex.Message, null); 
     typeof(T).GetProperty("ErrorCode").SetValue(result, ErrorCode, null); 
     return result; 
    } 
    return result; 
} 

Nếu bạn có thể sửa đổi BarBaz, sau đó bạn có thể cải thiện điều này bằng cách đặt một yêu cầu về T:

public interface IError 
{ 
    public string ErrorMessage { get; set; } 
    public int ErrorCode { get; set; } 
} 

T TryExecute<T>(Func<T> action, int ErrorCode) where T : IError 
{ 
    try 
    { 
     return action(); 
    } 
    catch (Exception ex) 
    { 
     result = Activator.CreateInstance<T>(); 
     result.ErrorMessage = ex.Message; 
     result.ErrorCode = ErrorCode; 
     return result; 
    } 
} 

Sau đó, bạn muốn sử dụng:

return TryExecute<Bar>(new Bar().Execute, Bar.ErrorCode); 

Và:

return TryExecute<Baz>(new Baz(stuff).Execute, Baz.ErrorCode); 

Điều đó có thể hoặc có thể không quá trừu tượng đối với thiết kế cụ thể của bạn; Ma quỷ là trong các chi tiết.

+0

Điều này trả về null nếu một ngoại lệ được ném – Lee

+0

@Xem ý tưởng là đặt * kết quả * thành đối tượng lỗi khi cần, trong khối * catch *. – McGarnagle

+0

Vâng, bạn không thể làm điều đó mà không có bất kỳ hạn chế về 'T' hoặc bất kỳ cách nào để tạo ra một. – Lee

2

Bạn có thực sự cần đăng nhập rõ ràng trong mọi phương pháp không? Thay vì có logic ngoại lệ trong mọi phương thức, có một trình xử lý trong phương thức Main của chương trình và xử lý các ngoại lệ một cách tổng quát.

Ngoài ra, bạn không cần phải trả lại đối tượng tùy ý từ khối catch nếu bạn thực sự cần đăng nhập ở đó, chỉ cần sử dụng throw; để cho phép nó di chuyển lên ngăn xếp.

+1

Có vẻ như câu trả lời đúng cho tôi, nhưng trong câu hỏi, người hỏi nói rằng anh ta bị hạn chế làm điều đó vì một lý do nào đó. – ametren

+0

Xem nhận xét được đề cập. Tôi bị hạn chế làm việc này. Cosider Foo là giao diện công cộng và tôi không có quyền kiểm soát để xử lý ngoại lệ ở cấp đó. – fin

+1

Được rồi, thông tin đó không có ở đó khi tôi viết câu trả lời của mình. – Femaref

5

Làm thế nào về một lớp cơ sở:

public class ErrorCapable { 
    public string ErrorMessage { set; get; } 
    public int ErrorCode { set; get; } 

    public static ErrorCapable<T> Oops(Exception exc) where T : ErrorCapable, new() { 
    // Code for logging error here 
    return new T() { ErrorMessage = exc.Message, ErrorCode = exc.ErrorCode }; 
    } 
} 

public class Bar : ErrorCapable { 
    //... 
} 
public class Baz : ErrorCapable { 
    //... 
} 

Sau đó, trong đánh bắt, chỉ cần sử dụng, ví dụ:

return ErrorCapable.Oops<Bar>(ex); 
2

Tôi nghĩ rằng tốt nhất bạn có thể làm là một cái gì đó như:

public T TryOrDefault<T>(Func<T> act, int errorCode, Func<BazException, T> onError) 
{ 
    try 
    { 
     return act; 
    } 
    catch(BazException ex) 
    { 
     WriteLogMessage(ex, errorCode); 
     return onError(ex); 
    } 
} 

sau đó bạn có thể viết các phương pháp khác của mình về điều này:

public Bar SomeMethodThatCanThrowException() 
{ 
    Bar b = new Bar(); 
    return ExecOrDefault(() => b.Execute(), Bar.ErrorCode, ex => new Bar { ErrorMessage = ex.Message, ErrorCode = Bar.ErrorCode }); 
}