2009-09-21 27 views
5

Hướng dẫn lập trình .NET nêu rõ rằng chúng ta không nên nắm bắt các ngoại lệ chung. Tôi giả sử các mã sau đây không phải là rất tốt vì chung loại ngoại lệ bắt:.NET Catch Exceptions

private object CreateObject(string classname) 
    { 
     object obj = null; 
     if (!string.IsNullOrEmpty(classname)) 
     { 
      try 
      { 
       System.Type oType = System.Type.GetTypeFromProgID(customClass); 
       obj = System.Activator.CreateInstance(oType); 
      } 
      catch (Exception ex) 
      { 
       Log.Error("Unable to create instance for COM Object with class name " + classname + "\n" + ex.Message); 
      } 
     } 
     return obj; 
    } 

Trong đoạn mã sau tôi bắt ngoại lệ đặc biệt nhưng không phải tất cả trong số họ và sau đó tôi lại ném ngoại lệ trong trường hợp là khác nhau từ các ngoại lệ không chung chung. Tuy nhiên, chức năng "CreateInstance" có thể ném nhiều ngoại lệ (ArgumentNullException, ArgumentException, NotSupportedException, TargetInvocationException, MethodAccessException, MemberAccessException, InvalidComObjectException, MissingMethodException, COMException, TypeLoadException).

Có thể chấp nhận tất cả ngoại lệ cá nhân khác không? đây có phải là cách tốt hơn không?

private object CreateObject(string classname) 
    { 
     object obj = null; 
     if (!string.IsNullOrEmpty(classname)) 
     { 
      try 
      { 
       System.Type oType = System.Type.GetTypeFromProgID(customClass); 
       obj = System.Activator.CreateInstance(oType); 
      } 
      catch (NotSupportedException ex) 
      { 
       Log.Error("...." + ex.Message); 
      } 
      catch (TargetInvocationException ex) 
      { 
       Log.Error("...." + ex.Message); 
      } 
      catch (COMException ex) 
      { 
       Log.Error("...." + ex.Message); 
      } 
      catch (TypeLoadException ex) 
      { 
       Log.Error("...." + ex.Message); 
      } 
      catch (InvalidComObjectException ex) 
      { 
       Log.Error("...." + ex.Message); 
      } 
      catch (Exception ex) 
      { 
       Log.Error("Unable to create instance for COM Object with class name " + classname + "\n" + ex.Message); 
       throw; 
      } 
     } 
     return obj; 
    } 
+0

Bản sao của: http://stackoverflow.com/questions/204814/is-there-any-valid-reason-to-ever-ignore-a-caught-exception –

Trả lời

9

Khá chấp nhận được ngoại lệ chung trong .NET 2+ của khuôn khổ.

- Chỉnh sửa

Lý do duy nhất bạn không làm là nếu bạn có thể làm điều gì đó khác với một ngoại lệ khác. Nếu bạn có kế hoạch xử lý tất cả như nhau, chỉ cần nắm bắt chung (hoặc một trong những cụ thể bạn đang sau, và để cho bất cứ điều gì khác đi lên).

+3

Tôi đồng ý. Đôi khi tôi cảm thấy chúng tôi không nhận được ngoại lệ chung này đối với những thái cực. –

+5

Tôi không đồng ý. Điều này sẽ khiến ứng dụng của bạn ở trạng thái ngoại lệ. Bạn chỉ nên xử lý các ngoại lệ mà bạn có thể làm gì đó. Đơn giản chỉ cần đăng nhập và di chuyển trên là một ý tưởng tồi. –

+0

@ justin.m.chase: Đó là một vị trí rất kỳ quặc. Vì vậy, bất cứ khi nào ứng dụng của bạn gặp sự cố, bạn muốn người dùng của mình thấy trang lỗi chung .net thay vì trang của riêng bạn? –

1

Tôi nghĩ rằng bạn có thể bắt tất cả ngoại lệ để chặn sự cố và hiển thị thông báo thân thiện với người dùng thay vì một số đầu ra đáng sợ.

Miễn là bạn không chỉ nuốt ngoại lệ, nhưng hãy ghi lại chúng và phản ứng với chúng một cách thích hợp trong nền.

1

Nếu bạn muốn làm điều gì đó đặc biệt với một loại ngoại lệ khác, hãy bắt nó trong một khối bắt riêng biệt. Nếu không thì sẽ vô nghĩa khi đăng nhập chúng một cách riêng biệt.

1

Không có một quy tắc cứng nhắc rằng chúng ta không nên sử dụng ngoại lệ chung nhưng phương châm nói rằng bất cứ khi nào chúng tôi có một lựa chọn để xử lý một loại hình cụ thể của ngoại lệ chúng ta không nên đi cho ngoại lệ chung ..

Nếu chúng tôi chắc chắn rằng tất cả các trường hợp ngoại lệ sẽ được xử lý theo cùng một cách, sau đó sử dụng ngoại lệ chung khác đi cho mỗi và mọi ngoại lệ cụ thể và chung chung nên đến cuối cùng cho một số ngoại lệ không rõ ..

và đôi khi trong ứng dụng của bạn bất kỳ ngoại lệ xảy ra không phải là được xử lý trong mã do xử lý ngoại lệ cụ thể sau đó ứng dụng của bạn có thể gặp sự cố.

do đó cách tiếp cận tốt hơn là xử lý tất cả ngoại lệ cụ thể và sau đó đưa ra một góc để ngoại lệ chung cũng như vậy mà ứng dụng vẫn ổn định mà không có tai nạn ..

và ngoại lệ chung không mong muốn có thể được báo cáo hoặc đăng nhập ở đâu đó để cải thiện phiên bản trong tương lai ..

11

Theo nguyên tắc chung, bạn không nên bắt ngoại lệ, trừ khi:

  1. bạn có một ngoại lệ cụ thể mà bạn có thể xử lý và làm điều gì đó về. Tuy nhiên trong trường hợp này, bạn nên luôn luôn kiểm tra xem bạn không nên cố gắng để tài khoản và tránh ngoại lệ ở nơi đầu tiên.

  2. Bạn đang ở cấp cao nhất của ứng dụng (ví dụ: Giao diện người dùng) và không muốn hiển thị hành vi mặc định cho người dùng. Ví dụ: bạn có thể muốn hộp thoại báo lỗi có thông báo kiểu "vui lòng gửi cho chúng tôi nhật ký của bạn".

  3. Bạn lại ném ngoại lệ sau khi xử lý bằng cách nào đó, ví dụ: nếu bạn quay trở lại giao dịch DB.

Trong ví dụ này, tại sao bạn lại bắt tất cả các loại khác nhau này? Dường như với tôi rằng mã của bạn chỉ có thể là:

try 
{ 
    System.Type oType = System.Type.GetTypeFromProgID(customClass); 
    return System.Activator.CreateInstance(oType); 
} 
catch (Exception ex) 
{ 
    Log.Error("...." + ex.Message); 

    //the generic catch is always fine if you then do this: 
    throw; 
} 

Vì vậy, vấn đề của bạn là một ví dụ về quy tắc (3) - bạn muốn ghi ngoại lệ, nhưng sau đó tiếp tục và ném nó lên.

Tất cả các loại khác nhau đều ở đó để trong một số trường hợp nhất định mà bạn biết bạn có thể xử lý (ví dụ: trường hợp 1). Ví dụ giả sử rằng bạn biết rằng có một cuộc gọi không được quản lý mà làm việc xung quanh COMException - sau đó mã của bạn trở thành:

try 
{ 
    System.Type oType = System.Type.GetTypeFromProgID(customClass); 
    return System.Activator.CreateInstance(oType); 
} 
catch (COMException cex) 
{ //deal with special case: 
    return LoadUnmanaged(); 
} 
catch (Exception ex) 
{ 
    Log.Error("...." + ex.Message); 

    //the generic catch is always fine if you then do this: 
    throw; 
} 
+0

Tôi bắt tất cả các loại ngoại lệ khác nhau vì chúng có thể xảy ra (ví dụ: sử dụng có thể chuyển tên lớp không được hỗ trợ hoặc không được đăng ký đúng cách) và trong trường hợp đó không có cách nào tốt để kiểm tra trước có thể kiểm tra ArgumentNullException). – Ioannis

+0

Tôi chắc chắn rằng tất cả chúng đều có thể xảy ra, nhưng trừ khi bạn định làm điều gì đó cụ thể cho loại ngoại lệ đó, bạn tốt hơn là chỉ bắt được 'Exception' chung và ném nó lên. – Keith

+1

Câu trả lời này là chính xác. Điều quan trọng là bạn đang ném ngoại lệ chung trở lên không nuốt chúng. –

1

Nó được coi là một thực hành kém để có thói quen bắt đáy Exception khi giao dịch với các lỗi xử lý, bởi vì nó cho thấy một tiềm năng thiếu hiểu biết về những gì bạn đang thực sự xử lý. Khi bạn nhìn thấy một khối mã bắt, hãy đọc số Exception, "nếu có gì sai ở đây, hãy thực hiện điều này", trong đó bất kỳ thứ gì có thể nằm trong khoảng từ NullReferenceException đến số OutOfMemoryException.

Sự nguy hiểm trong việc xử lý tất cả các lỗi giống nhau, có nghĩa là bạn không quan tâm đến mức độ nghiêm trọng của lỗi hoặc cách bạn có thể giải quyết lỗi. 99% thời gian, khi tôi nhìn thấy mã số catch(Exception ex), nó ngay lập tức theo sau bởi mã mà nuốt ngoại lệ, không có đầu mối để thực sự tại sao các báo cáo không thành công. Ugh.

Ví dụ về ghi nhật ký lỗi hiển thị đúng cách để sử dụng cơ sở Exception, trong trường hợp bạn thực sự muốn xử lý tất cả các ngoại lệ giống nhau, thường ở cấp cao nhất của ứng dụng để ngăn ứng dụng chấm dứt trong một xấu xí lộn xộn.

1

Tôi đồng ý với câu trả lời của Keith. Vấn đề thực sự với cả hai ví dụ mã là chúng có thể trả về null. Bạn có một phương thức gọi là CreateObject trả về một thể hiện của đối tượng kiểu. Phương pháp đó không thành công vì một số lý do, chẳng hạn như COMException. Cả hai ví dụ mã sau đó sẽ trả về null. Mã gọi hiện chịu trách nhiệm kiểm tra kết quả so với null, hoặc nó sẽ ném một NullReferenceException.

Trích dẫn Framework Design Guidelines 2nd ed.:

Nếu một thành viên không thể làm thành công những gì nó được thiết kế để làm, nó nên được coi là một thất bại thực hiện, và một ngoại lệ nên được ném.

Nó không làm theo tên của nó. Ném đi!

Mã của Keith là tốt; có thể đăng nhập ngoại lệ và trả lại.

Đây là một phương pháp riêng tư, vì vậy được cho là nguyên tắc thiết kế không áp dụng. Tôi vẫn áp dụng chúng ở đây, mặc dù - tại sao xả mã của bạn với "if (result == null)" khi bạn có thể sử dụng xử lý ngoại lệ thay thế?

+0

Đôi khi hữu ích khi có một phương thức sẽ cố gắng tạo một đối tượng, nhưng trả về null nếu nó không thể; nó thường là một ý tưởng tốt, tuy nhiên, để cung cấp cho các phương pháp một tên cho thấy đó là những gì nó sẽ làm. Microsoft thích có các thường trình trả về một boolean thành công/thất bại và có tham số byref một biến tham chiếu đối tượng để lưu trữ đối tượng mới, nhưng tôi muốn trả về giá trị hoặc null, vì nó cho phép một định nghĩa một biến mới trong câu lệnh cố gắng tạo ra cá thể. – supercat

+0

@supercat: Sẽ tốt nếu chúng ta có thể tin tưởng các giá trị trả về là null ... – TrueWill

+0

Nếu gọi một hàm gọi là TryGetFoo mà không xem xét nó có thể thất bại, chúng xứng đáng với những gì chúng nhận được. Họ thậm chí có thể dễ dàng bỏ qua giá trị trả về (cờ thành công/thất bại) từ phương thức "TryGetFoo" kiểu MS. Phiên bản MS-pattern có một tính năng trong một số tình huống có thể là một điều tốt hay xấu: có thể cho một hàm để ném một ngoại lệ nhưng vẫn đặt biến được truyền vào một cái gì đó trước khi nó làm như vậy. Điều này có thể tốt nếu biến là IDisposable, nhưng có thể gây nhầm lẫn nếu mã trả về của phương thức create bị bỏ qua. – supercat