Cách sử dụng thử bắt để xử lý ngoại lệ là cách tốt nhất


201

trong khi duy trì mã của đồng nghiệp của tôi từ ngay cả một người tự nhận là nhà phát triển cấp cao, tôi thường thấy mã sau:

try
{
  //do something
}
catch
{
  //Do nothing
}

hoặc đôi khi họ viết thông tin đăng nhập vào tệp nhật ký như try catchkhối sau

try
{
  //do some work
}
catch(Exception exception)
{
   WriteException2LogFile(exception);
}

Tôi chỉ tự hỏi nếu những gì họ đã làm là thực hành tốt nhất? Nó làm tôi bối rối vì trong suy nghĩ của tôi, người dùng nên biết những gì xảy ra với hệ thống.

Xin vui lòng cho tôi một lời khuyên.


128
Đoạn trích số 1 là 99,999% thời gian không được chấp nhận.
leppie

22
Hiển thị ngoại lệ trực tiếp cho người dùng không bao giờ là một ý tưởng tốt chủ yếu vì hai lý do: 1. nếu đó là người dùng thông thường, anh ta sẽ cảm thấy khó chịu khi đọc thông báo lỗi mà rất ít thông báo cho anh ấy / cô ấy. 2. nếu anh ta, được gọi là, hacker có thể nhận được thông tin hữu ích. Cách thực hành tốt nhất, IMO, là ghi nhật ký ngoại lệ và hiển thị thông báo lỗi thân thiện.
Leri

4
@leppie Nếu có điều gì đó bất ngờ xảy ra (như NullReferencehoặc ArgumentNullđó không phải là một phần của luồng ứng dụng), điều đó có nghĩa là có một lỗi cần phải sửa, vì vậy việc đăng nhập chúng sẽ giúp gỡ lỗi mã của bạn nhanh hơn nhiều.
Leri

14
Sử dụng một khối thử bắt để ẩn một ngoại lệ thường là kết quả của việc lập trình lười biếng. Đó là một phím tắt thường được sử dụng thay vì viết mã xác thực để kiểm tra đầu vào. Rất hiếm khi có một số trường hợp ngoại lệ có thể phát sinh mà không ảnh hưởng đến hoạt động của mã của bạn và việc ẩn nó như thế này có thể ổn. Điều này là khá hiếm tuy nhiên.
Corey

12
@Toan, tốt, nếu đó là một công việc hàng loạt, tôi đang bắt ở cấp cao nhất (Chính) để đăng nhập, và sau đó suy nghĩ lại để đặt ra một báo động rằng công việc bị chấm dứt bất thường. Nếu đó là một ứng dụng web, tôi sẽ để bong bóng ngoại lệ cho trình xử lý toàn cầu, ghi nhật ký và sau đó chuyển hướng người dùng đến màn hình lỗi. Kịch bản ca sử dụng của bạn chỉ ra những gì bạn làm với ngoại lệ đó sau khi bạn đã đăng nhập hoặc xử lý nó.
Anthony Pegram

Câu trả lời:


300

Chiến lược xử lý ngoại lệ của tôi là:

  • Để nắm bắt tất cả các ngoại lệ chưa được xử lý bằng cách nối vào Application.ThreadException event, sau đó quyết định:

    • Đối với ứng dụng UI: để bật nó cho người dùng bằng thông báo xin lỗi (winforms)
    • Đối với Dịch vụ hoặc ứng dụng Bảng điều khiển: đăng nhập tệp vào tệp (dịch vụ hoặc bảng điều khiển)

Sau đó, tôi luôn luôn kèm theo mỗi đoạn mã được chạy từ bên ngoài vào try/catch:

  • Tất cả các sự kiện được kích hoạt bởi cơ sở hạ tầng Winforms (Tải, nhấp, đã chọn Thay đổi ...)
  • Tất cả các sự kiện được kích hoạt bởi các thành phần bên thứ ba

Sau đó, tôi gửi kèm theo 'thử / bắt'

  • Tất cả các hoạt động mà tôi biết có thể không hoạt động mọi lúc (Hoạt động IO, tính toán với phân chia số 0 tiềm năng ...). Trong trường hợp như vậy, tôi ném một cái mới ApplicationException("custom message", innerException)để theo dõi những gì thực sự đã xảy ra

Ngoài ra, tôi cố gắng hết sức để sắp xếp ngoại lệ một cách chính xác . Có những trường hợp ngoại lệ:

  • cần phải được hiển thị cho người dùng ngay lập tức
  • yêu cầu một số xử lý bổ sung để đặt mọi thứ lại với nhau khi chúng xảy ra để tránh các vấn đề xếp tầng (ví dụ: đặt .EndUpdate trong finallyphần trong khi TreeViewđiền)
  • Người dùng không quan tâm, nhưng điều quan trọng là phải biết những gì đã xảy ra. Vì vậy, tôi luôn luôn đăng nhập chúng:

    • Trong nhật ký sự kiện
    • hoặc trong tệp .log trên đĩa

Đó là một thực tiễn tốt để thiết kế một số phương thức tĩnh để xử lý các trường hợp ngoại lệ trong trình xử lý lỗi cấp cao nhất của ứng dụng.

Tôi cũng buộc mình phải cố gắng:

  • Hãy nhớ TẤT CẢ các ngoại lệ được sủi bọt lên đến cấp cao nhất . Không cần thiết phải đặt xử lý ngoại lệ ở khắp mọi nơi.
  • Các hàm được gọi lại hoặc được gọi sâu không cần hiển thị hoặc ghi nhật ký ngoại lệ: chúng được tự động nổi lên hoặc được lưu lại với một số thông báo tùy chỉnh trong trình xử lý ngoại lệ của tôi.

Cuối cùng thì :

Xấu:

// DON'T DO THIS, ITS BAD
try
{
    ...
}
catch 
{
   // only air...
}

Vô ích:

// DONT'T DO THIS, ITS USELESS
try
{
    ...
}
catch(Exception ex)
{
    throw ex;
}

Có một thử cuối cùng mà không bắt được là hoàn toàn hợp lệ:

try
{
    listView1.BeginUpdate();

    // If an exception occurs in the following code, then the finally will be executed
    // and the exception will be thrown
    ...
}
finally
{
    // I WANT THIS CODE TO RUN EVENTUALLY REGARDLESS AN EXCEPTION OCCURED OR NOT
    listView1.EndUpdate();
}

Những gì tôi làm ở cấp cao nhất:

// i.e When the user clicks on a button
try
{
    ...
}
catch(Exception ex)
{
    ex.Log(); // Log exception

    -- OR --

    ex.Log().Display(); // Log exception, then show it to the user with apologies...
}

Những gì tôi làm trong một số chức năng được gọi là:

// Calculation module
try
{
    ...
}
catch(Exception ex)
{
    // Add useful information to the exception
    throw new ApplicationException("Something wrong happened in the calculation module :", ex);
}

// IO module
try
{
    ...
}
catch(Exception ex)
{
    throw new ApplicationException(string.Format("I cannot write the file {0} to {1}", fileName, directoryName), ex);
}

Có rất nhiều việc phải làm với xử lý ngoại lệ (Ngoại lệ tùy chỉnh) nhưng các quy tắc tôi cố gắng ghi nhớ là đủ cho các ứng dụng đơn giản tôi làm.

Dưới đây là một ví dụ về các phương thức tiện ích mở rộng để xử lý các ngoại lệ bị bắt một cách thoải mái. Chúng được thực hiện theo cách chúng có thể được kết nối với nhau và rất dễ dàng để thêm xử lý ngoại lệ bắt được của riêng bạn.

// Usage:

try
{
    // boom
}
catch(Exception ex)
{
    // Only log exception
    ex.Log();

    -- OR --

    // Only display exception
    ex.Display();

    -- OR --

    // Log, then display exception
    ex.Log().Display();

    -- OR --

    // Add some user-friendly message to an exception
    new ApplicationException("Unable to calculate !", ex).Log().Display();
}

// Extension methods

internal static Exception Log(this Exception ex)
{
    File.AppendAllText("CaughtExceptions" + DateTime.Now.ToString("yyyy-MM-dd") + ".log", DateTime.Now.ToString("HH:mm:ss") + ": " + ex.Message + "\n" + ex.ToString() + "\n");
    return ex;
}

internal static Exception Display(this Exception ex, string msg = null, MessageBoxImage img = MessageBoxImage.Error)
{
    MessageBox.Show(msg ?? ex.Message, "", MessageBoxButton.OK, img);
    return ex;
}

98
catch(Exception ex) { throw ex; }trong C # tệ hơn dự phòng (bất kể loại ngoại lệ bạn đang bắt). Để suy nghĩ lại, sử dụng throw;. Với cái trước, ngoại lệ sẽ trông giống như nó bắt nguồn từ của bạn throw extrong khi với cái sau, nó sẽ bắt nguồn chính xác từ throwcâu lệnh gốc .
một CVn

2
Tại sao bạn móc Application.ThreadExceptionsự kiện bọc mọi ngoại lệ bằng a catch(Exception ex) {ex.Log(ex);}. Tôi có thể đồng ý rằng cái trước là một thực tiễn tuyệt vời, nhưng cái sau có thêm rủi ro sao chép nhật ký lỗi của bạn và che giấu rằng ngoại lệ đã xảy ra. Cũng throw exrất rất xấu.
Keith

1
Tôi hiểu về bắt (Exception ex) {throw ex; } là vô dụng. Vì vậy, tôi cho rằng "dư thừa" không phải là từ tốt nhất để nói "Đừng làm điều này". Đó là lý do tại sao tôi thay đổi bài đăng một chút để nói rõ hơn rằng hai ví dụ đầu tiên về thử bắt phải tránh.
Larry

3
Câu trả lời tuyệt vời và mang tính xây dựng, hầu hết tôi rất thích cụm từ Only air :) Và cảm ơn vì Application.ThreadExceptionsự kiện này, tôi đã không nhận ra điều đó, rất hữu ích.
Mahdi Tahsildari


61

Thực hành tốt nhất là xử lý ngoại lệ không bao giờ nên che giấu các vấn đề . Điều này có nghĩa là try-catchcác khối nên cực kỳ hiếm.

Có 3 trường hợp sử dụng một try-catchý nghĩa.

  1. Luôn luôn đối phó với các trường hợp ngoại lệ được biết đến ở mức thấp nhất có thể. Tuy nhiên, nếu bạn đang mong đợi một ngoại lệ, thông thường nên thử nghiệm nó trước. Đối với phân tích ví dụ, định dạng và ngoại lệ số học hầu như luôn được xử lý tốt hơn bằng cách kiểm tra logic trước, thay vì cụ thể try-catch.

  2. Nếu bạn cần làm gì đó với một ngoại lệ (ví dụ: ghi nhật ký hoặc quay lại giao dịch) thì hãy ném lại ngoại lệ đó.

  3. Luôn luôn xử lý các trường hợp ngoại lệ không xác định cao nhất có thể - mã duy nhất nên sử dụng ngoại lệ và không ném lại phải là UI hoặc API công khai.

Giả sử bạn đang kết nối với API từ xa, ở đây bạn biết sẽ gặp phải một số lỗi nhất định (và có những điều xảy ra trong những trường hợp đó), vì vậy đây là trường hợp 1:

try 
{
    remoteApi.Connect()
}
catch(ApiConnectionSecurityException ex) 
{
    // User's security details have expired
    return false;
}

return true;

Lưu ý rằng không có ngoại lệ khác được bắt gặp, vì chúng không được mong đợi.

Bây giờ giả sử rằng bạn đang cố lưu một cái gì đó vào cơ sở dữ liệu. Chúng tôi phải khôi phục lại nếu thất bại, vì vậy chúng tôi có trường hợp 2:

try
{
    DBConnection.Save();
}
catch
{
    // Roll back the DB changes so they aren't corrupted on ANY exception
    DBConnection.Rollback();

    // Re-throw the exception, it's critical that the user knows that it failed to save
    throw;
}

Lưu ý rằng chúng tôi ném lại ngoại lệ - mã cao hơn vẫn cần biết rằng đã xảy ra lỗi.

Cuối cùng, chúng ta có UI - ở đây chúng ta không muốn có các ngoại lệ hoàn toàn chưa được xử lý, nhưng chúng ta cũng không muốn che giấu chúng. Ở đây chúng tôi có một ví dụ về trường hợp 3:

try
{
    // Do something
}
catch(Exception ex) 
{
    // Log exception for developers
    WriteException2LogFile(ex);

    // Display message to users
    DisplayWarningBox("An error has occurred, please contact support!");
}

Tuy nhiên, hầu hết các khung API hoặc UI đều có cách thực hiện chung 3. Ví dụ, ASP.Net có màn hình lỗi màu vàng, bỏ các chi tiết ngoại lệ, nhưng có thể được thay thế bằng một thông báo chung hơn trong môi trường sản xuất. Theo đó là cách tốt nhất vì nó giúp bạn tiết kiệm rất nhiều mã, nhưng cũng vì ghi nhật ký và hiển thị lỗi nên là các quyết định cấu hình thay vì mã hóa cứng.

Tất cả điều này có nghĩa là cả trường hợp 1 (trường hợp ngoại lệ đã biết) và trường hợp 3 (xử lý giao diện người dùng một lần) đều có các mẫu tốt hơn (tránh lỗi dự kiến ​​hoặc xử lý lỗi tay cho UI).

Ngay cả trường hợp 2 có thể được thay thế bằng các mẫu tốt hơn, ví dụ phạm vi giao dịch ( usingcác khối phục hồi bất kỳ giao dịch nào không được cam kết trong khối) khiến các nhà phát triển khó có thể hiểu sai mẫu thực tiễn tốt nhất.

Ví dụ, giả sử bạn có một ứng dụng ASP.Net quy mô lớn. Lỗi ghi nhật ký có thể thông qua ELMAH , hiển thị lỗi có thể là một YSoD thông tin cục bộ và một thông báo cục bộ tốt đẹp trong sản xuất. Tất cả các kết nối cơ sở dữ liệu có thể được thông qua phạm vi giao dịch và usingkhối. Bạn không cần một try-catchkhối duy nhất .

TL; DR: Thực tế tốt nhất là không sử dụng try-catchcác khối.


4
@Jorj bạn nên đọc toàn bộ bài viết, và nếu bạn vẫn không đồng ý có lẽ sẽ có tính xây dựng hơn để chống lại một trong những lập luận hỗ trợ của tôi thay vì chỉ nói rằng bạn không thích kết luận của tôi. Gần như luôn luôn có một mô hình tốt hơn try-catch- đôi khi nó có thể hữu ích và tôi không cho rằng bạn không bao giờ nên sử dụng chúng, nhưng 99% thời gian có cách tốt hơn.
Keith

Câu trả lời hay nhất cho đến nay - gần như mọi loại phát triển .net đều có một HandLER thuộc loại nào đó phù hợp hơn nhiều để xử lý các trường hợp ngoại lệ ở cấp độ toàn cầu, giúp xử lý chúng một cách dễ dàng hơn cũng như làm cho nó dễ dàng hơn nhiều đang phát triển (tại sao mọi người muốn đào một tệp nhật ký để theo dõi ngăn xếp ??) @Kieth, tôi sẽ dẫn đầu với TLDR của bạn và thêm vào một số ví dụ về trình xử lý toàn cầu (ví dụ ThreadException, Application_Error, v.v.). Bằng mọi cách, bắt các lỗi CỤ THỂ, nhưng thật điên rồ khi bao bọc phương thức trong một lần thử / bắt / đăng nhập
b_levitt

34

Một ngoại lệ là một lỗi chặn .

Trước hết, cách tốt nhất là không được ném ngoại lệ cho bất kỳ loại lỗi nào, trừ khi đó là lỗi chặn .

Nếu lỗi là chặn , sau đó ném ngoại lệ. Một khi ngoại lệ đã được ném, không cần phải che giấu nó bởi vì nó là ngoại lệ; cho người dùng biết về nó (bạn nên định dạng lại toàn bộ ngoại lệ cho thứ gì đó hữu ích cho người dùng trong UI).

Công việc của bạn là nhà phát triển phần mềm là nỗ lực ngăn chặn một trường hợp đặc biệt trong đó một số tình huống tham số hoặc thời gian chạy có thể kết thúc bằng một ngoại lệ. Đó là, các ngoại lệ không được tắt tiếng, nhưng chúng phải được tránh .

Ví dụ: nếu bạn biết rằng một số đầu vào số nguyên có thể có định dạng không hợp lệ, hãy sử dụng int.TryParsethay vì int.Parse. Có rất nhiều trường hợp bạn có thể làm điều này thay vì chỉ nói "nếu thất bại, chỉ cần ném một ngoại lệ".

Ném ngoại lệ là đắt tiền.

Rốt cuộc, nếu một ngoại lệ được ném ra, thay vì viết ngoại lệ vào nhật ký một khi nó đã bị ném, một trong những thực tiễn tốt nhất là bắt nó trong một trình xử lý ngoại lệ cơ hội đầu tiên . Ví dụ:

  • ASP.NET: Global.asax Application_Error
  • Thứ khác: sự kiện AppDomain.FirstChanceException .

Quan điểm của tôi là các thử / bắt cục bộ phù hợp hơn để xử lý các trường hợp đặc biệt trong đó bạn có thể dịch một ngoại lệ sang một trường hợp khác hoặc khi bạn muốn "tắt tiếng" nó cho một trường hợp rất, rất, rất, rất đặc biệt (một lỗi thư viện ném một ngoại lệ không liên quan mà bạn cần tắt tiếng để khắc phục toàn bộ lỗi).

Đối với phần còn lại của các trường hợp:

  • Cố gắng tránh ngoại lệ.
  • Nếu điều này là không thể: xử lý ngoại lệ cơ hội đầu tiên.
  • Hoặc sử dụng khía cạnh PostSharp (AOP).

Trả lời @thewhiteambit về một số bình luận ...

@thewhiteambit nói:

Ngoại lệ không phải là Lỗi nghiêm trọng, chúng là Ngoại lệ! Đôi khi, chúng thậm chí không phải là Lỗi, nhưng để xem xét chúng là Lỗi nghiêm trọng - Lỗi là sự hiểu biết hoàn toàn sai về Ngoại lệ là gì.

Trước hết, làm thế nào một ngoại lệ thậm chí không thể là một lỗi?

  • Không có kết nối cơ sở dữ liệu => ngoại lệ.
  • Định dạng chuỗi không hợp lệ để phân tích thành một số loại => ngoại lệ
  • Cố gắng phân tích JSON và trong khi đầu vào không thực sự là JSON => ngoại lệ
  • Đối số nulltrong khi đối tượng được mong đợi => ngoại lệ
  • Một số thư viện có lỗi => ném ngoại lệ không mong muốn
  • Có một kết nối ổ cắm và nó bị ngắt kết nối. Sau đó, bạn cố gắng gửi tin nhắn => ngoại lệ
  • ...

Chúng tôi có thể liệt kê 1k trường hợp khi một ngoại lệ được đưa ra và sau tất cả, bất kỳ trường hợp nào có thể sẽ là một lỗi .

Một ngoại lệ một lỗi, bởi vì vào cuối ngày, nó là một đối tượng thu thập thông tin chẩn đoán - nó có một thông báo và nó xảy ra khi có sự cố.

Không ai sẽ ném ngoại lệ khi không có trường hợp ngoại lệ. Các ngoại lệ nên chặn lỗi vì một khi chúng bị ném, nếu bạn không cố gắng sử dụng thử / bắt và ngoại lệ để thực hiện luồng kiểm soát, điều đó có nghĩa là ứng dụng / dịch vụ của bạn sẽ dừng hoạt động được đưa vào một trường hợp đặc biệt .

Ngoài ra, tôi đề nghị mọi người nên kiểm tra mô hình thất bại nhanh chóng được xuất bản bởi Martin Fowler (và được viết bởi Jim Shore) . Đây là cách tôi luôn hiểu cách xử lý các trường hợp ngoại lệ, ngay cả trước khi tôi nhận được tài liệu này một thời gian trước đây.

[...] coi chúng là Tử vong - Lỗi là sự hiểu biết hoàn toàn sai về ngoại lệ là gì.

Thông thường các ngoại lệ cắt giảm một số luồng hoạt động và chúng được xử lý để chuyển đổi chúng thành các lỗi có thể hiểu được của con người. Do đó, có vẻ như một ngoại lệ thực sự là một mô hình tốt hơn để xử lý các trường hợp lỗi và xử lý chúng để tránh sự cố hoàn toàn cho ứng dụng / dịch vụ và thông báo cho người dùng / người tiêu dùng rằng đã xảy ra sự cố.

Thêm câu trả lời về mối quan tâm @thewhiteambit

Ví dụ, trong trường hợp Thiếu kết nối cơ sở dữ liệu, chương trình có thể tiếp tục đặc biệt bằng cách ghi vào tệp cục bộ và gửi các thay đổi tới Cơ sở dữ liệu sau khi có sẵn một lần nữa. Việc truyền chuỗi String-To-Number không hợp lệ của bạn có thể được thử phân tích lại bằng cách diễn giải ngôn ngữ địa phương trên Exception, giống như khi bạn thử ngôn ngữ tiếng Anh mặc định thành Parse ("1,5") và bạn thử lại với phiên dịch tiếng Đức hoàn toàn tốt vì chúng tôi sử dụng dấu phẩy thay vì điểm như dấu phân cách. Bạn thấy các Ngoại lệ này thậm chí không được chặn, chúng chỉ cần xử lý Ngoại lệ.

  1. Nếu ứng dụng của bạn có thể hoạt động ngoại tuyến mà không lưu dữ liệu vào cơ sở dữ liệu, bạn không nên sử dụng ngoại lệ , vì việc triển khai luồng kiểm soát sử dụng try/catchđược coi là một mô hình chống. Công việc ngoại tuyến là trường hợp sử dụng có thể, vì vậy bạn triển khai luồng kiểm soát để kiểm tra xem cơ sở dữ liệu có thể truy cập được hay không, bạn không đợi cho đến khi không thể truy cập được .

  2. Điều phân tích cú pháp cũng là một trường hợp dự kiến ​​( không phải TRƯỜNG HỢP TUYỆT VỜI ). Nếu bạn mong đợi điều này, bạn không sử dụng ngoại lệ để thực hiện kiểm soát luồng! . Bạn nhận được một số siêu dữ liệu từ người dùng để biết văn hóa của anh ấy / cô ấy và bạn sử dụng trình định dạng cho việc này! .NET cũng hỗ trợ môi trường này và các môi trường khác, và một ngoại lệ vì phải tránh định dạng số nếu bạn mong muốn sử dụng ứng dụng / dịch vụ cụ thể theo văn hóa của bạn .

Một ngoại lệ chưa được xử lý thường trở thành một Lỗi, nhưng bản thân Ngoại lệ không phải là codeproject.com/Articles/15921/Not- ALL-Exceptions-Are-Errors

Bài viết này chỉ là một ý kiến ​​hoặc quan điểm của tác giả.

Vì Wikipedia cũng có thể chỉ là ý kiến ​​của tác giả phát âm, tôi sẽ không nói đó là giáo điều , nhưng hãy kiểm tra Mã hóa bằng bài báo ngoại lệ nói ở đâu đó trong đoạn nào đó:

[...] Sử dụng các ngoại lệ này để xử lý các lỗi cụ thể phát sinh để tiếp tục chương trình được gọi là mã hóa bằng ngoại lệ. Mô hình chống này có thể nhanh chóng làm giảm phần mềm về hiệu suất và khả năng bảo trì.

Nó cũng nói ở đâu đó:

Sử dụng ngoại lệ không chính xác

Thông thường mã hóa bằng ngoại lệ có thể dẫn đến các vấn đề tiếp theo trong phần mềm với việc sử dụng ngoại lệ không chính xác. Ngoài việc sử dụng xử lý ngoại lệ cho một vấn đề duy nhất, việc sử dụng ngoại lệ không chính xác còn làm điều này thêm bằng cách thực thi mã ngay cả sau khi ngoại lệ được đưa ra. Phương pháp lập trình kém này giống với phương pháp goto trong nhiều ngôn ngữ phần mềm nhưng chỉ xảy ra sau khi phát hiện sự cố trong phần mềm.

Thành thật mà nói, tôi tin rằng phần mềm không thể được phát triển không coi trọng các trường hợp sử dụng. Nếu bạn biết rằng ...

  • Cơ sở dữ liệu của bạn có thể ngoại tuyến ...
  • Một số tập tin có thể bị khóa ...
  • Một số định dạng có thể không được hỗ trợ ...
  • Một số xác thực tên miền có thể thất bại ...
  • Ứng dụng của bạn sẽ hoạt động ở chế độ ngoại tuyến ...
  • bất cứ trường hợp sử dụng nào ...

... bạn sẽ không sử dụng ngoại lệ cho điều đó . Bạn sẽ hỗ trợ các trường hợp sử dụng này bằng cách sử dụng luồng điều khiển thường xuyên.

Và nếu một số trường hợp sử dụng không mong muốn không được bảo vệ, mã của bạn sẽ thất bại nhanh chóng, bởi vì nó sẽ đưa ra một ngoại lệ . Phải, bởi vì một ngoại lệ là một trường hợp đặc biệt .

Mặt khác, và cuối cùng, đôi khi bạn bao gồm các trường hợp ngoại lệ , ném ra các ngoại lệ dự kiến , nhưng bạn không ném chúng để thực hiện luồng kiểm soát. Bạn làm điều đó bởi vì bạn muốn thông báo cho các lớp trên rằng bạn không hỗ trợ một số trường hợp sử dụng hoặc mã của bạn không hoạt động với một số đối số hoặc dữ liệu / thuộc tính môi trường nhất định.


6

Lần duy nhất bạn nên lo lắng cho người dùng của mình về điều gì đó đã xảy ra trong mã là nếu có điều gì đó họ có thể hoặc cần làm để tránh sự cố. Nếu họ có thể thay đổi dữ liệu trên một biểu mẫu, nhấn nút hoặc thay đổi cài đặt ứng dụng để tránh sự cố thì hãy cho họ biết. Nhưng những cảnh báo hoặc lỗi mà người dùng không có khả năng tránh chỉ khiến họ mất niềm tin vào sản phẩm của bạn.

Ngoại lệ và Nhật ký dành cho bạn, nhà phát triển, không phải người dùng cuối của bạn. Hiểu đúng việc cần làm khi bạn nắm bắt từng ngoại lệ tốt hơn nhiều so với việc chỉ áp dụng một số quy tắc vàng hoặc dựa vào mạng lưới an toàn trên toàn ứng dụng.

Mã hóa không suy nghĩ là loại mã hóa DUY NHẤT. Việc bạn cảm thấy có điều gì đó tốt hơn có thể được thực hiện trong những tình huống đó cho thấy rằng bạn được đầu tư vào tiền mã hóa tốt, nhưng tránh cố gắng đóng dấu một số quy tắc chung trong những tình huống này và hiểu lý do khiến thứ gì đó bị ném ngay từ đầu và những gì bạn có thể làm để phục hồi từ nó.


6

Tôi biết đây là một câu hỏi cũ, nhưng không ai ở đây đề cập đến bài viết MSDN, và đó là tài liệu thực sự đã xóa nó cho tôi, MSDN có một tài liệu rất tốt về điều này, bạn nên nắm bắt ngoại lệ khi các điều kiện sau là đúng:

  • Bạn hiểu rõ lý do tại sao ngoại lệ có thể bị ném và bạn có thể thực hiện khôi phục cụ thể, chẳng hạn như nhắc người dùng nhập tên tệp mới khi bạn bắt đối tượng FileNotFoundException.

  • Bạn có thể tạo và ném một ngoại lệ mới, cụ thể hơn.

int GetInt(int[] array, int index)
{
    try
    {
        return array[index];
    }
    catch(System.IndexOutOfRangeException e)
    {
        throw new System.ArgumentOutOfRangeException(
            "Parameter index is out of range.");
    }
}
  • Bạn muốn xử lý một phần ngoại lệ trước khi chuyển qua để xử lý bổ sung. Trong ví dụ sau, một khối bắt được sử dụng để thêm một mục vào nhật ký lỗi trước khi ném lại ngoại lệ.
    try
{
    // Try to access a resource.
}
catch (System.UnauthorizedAccessException e)
{
    // Call a custom error logging procedure.
    LogError(e);
    // Re-throw the error.
    throw;     
}

Tôi khuyên bạn nên đọc toàn bộ phần " Xử lý ngoại lệ và xử lý ngoại lệ " và cả Thực tiễn tốt nhất cho ngoại lệ .


1

Cách tiếp cận tốt hơn là cách thứ hai (cách mà bạn chỉ định loại ngoại lệ). Ưu điểm của việc này là bạn biết rằng loại ngoại lệ này có thể xảy ra trong mã của bạn. Bạn đang xử lý loại ngoại lệ này và bạn có thể tiếp tục. Nếu có bất kỳ ngoại lệ nào khác xảy ra, thì điều đó có nghĩa là có gì đó không ổn sẽ giúp bạn tìm ra lỗi trong mã của mình. Ứng dụng cuối cùng sẽ bị sập, nhưng bạn sẽ biết rằng có một cái gì đó bạn đã bỏ lỡ (lỗi) cần được sửa chữa.


1

Với Ngoại lệ, tôi thử các cách sau:

Đầu tiên, tôi bắt các loại ngoại lệ đặc biệt như chia cho 0, các hoạt động IO, v.v. và viết mã theo đó. Ví dụ: phép chia cho số 0, tùy thuộc vào mức độ chứng minh của các giá trị mà tôi có thể cảnh báo cho người dùng (ví dụ: một máy tính đơn giản trong phép tính trung bình (không phải là đối số) đến phân chia bằng 0) hoặc âm thầm xử lý ngoại lệ đó, ghi nhật ký nó và tiếp tục xử lý.

Sau đó, tôi cố gắng để bắt các ngoại lệ còn lại và đăng nhập chúng. Nếu có thể cho phép thực thi mã, nếu không thì cảnh báo người dùng rằng đã xảy ra lỗi và yêu cầu họ gửi báo cáo lỗi.

Trong mã, một cái gì đó như thế này:

try{
    //Some code here
}
catch(DivideByZeroException dz){
    AlerUserDivideByZerohappened();
}
catch(Exception e){
    treatGeneralException(e);
}
finally{
    //if a IO operation here i close the hanging handlers for example
}

1
Chia cho các ngoại lệ bằng không và tương tự được xử lý tốt hơn bằng cách kiểm tra 0tử số trước, thay vì a try-catch. Ngoài ra tại sao bắt chung chung Exceptionở đây? Tốt hơn hết là bạn nên để lỗi xảy ra hơn là xử lý ở đây trong mọi trường hợp mà bạn không mong đợi.
Keith

Đọc tốt hơn những gì tôi đã viết về ví dụ tôi đã đưa ra - chú ý "không tranh luận". Tất nhiên bất kỳ máy tính nên xác minh các đối số nhất định. Những gì tôi nói là về các bước giữa. Tại thời điểm đó, xác minh đối số người dùng đã xảy ra. Ngoài ra trong một số ứng dụng, tốt hơn hết là tránh các ngoại lệ để nổi bong bóng. Một số ứng dụng nên xử lý ngoại lệ một cách im lặng, trong khi những ứng dụng khác nên coi ngoại lệ là lỗi. Một ví dụ về máy chủ web sẽ chạy ngay cả khi có ngoại lệ xảy ra, trong đó phần mềm y tế (ví dụ máy x-quang) sẽ hủy bỏ khi xảy ra ngoại lệ.
Sorcerer86pt

Không có ứng dụng nên bao giờ đối xử với ngoại lệ âm thầm. Đôi khi bạn có một ngoại lệ mà mã có thể xử lý, nhưng việc sử dụng như vậy phải vừa hiếm vừa cụ thể đối với ngoại lệ dự kiến. Ví dụ về máy chủ web của bạn là một máy chủ kém - nó phải có cài đặt cấu hình cho phép bạn chọn cách ghi lỗi và liệu chúng có được hiển thị chi tiết hay chỉ là trang HTTP 500, nhưng chúng không bao giờ nên âm thầm bỏ qua lỗi.
Keith

Tôi đang cố gắng tìm ra điều gì thực sự thúc đẩy mọi người thêm một từ đồng nghĩa cho "goto". Nhưng đối với việc chia cho số 0, đây sẽ là một loại ngoại lệ mà tôi có thể thấy, điều đó biện minh cho việc cải thiện ngôn ngữ. Tại sao? Bởi vì đó có thể là trường hợp A) zero về mặt thống kê là vô hạn trong tập dữ liệu của bạn và B) sử dụng (cho phép) một ngoại lệ có thể hiệu quả hơn nhiều, bởi vì thực hiện phép chia là một cách để kiểm tra ước số 0. Khi A và B đúng, việc thực thi chương trình trung bình của bạn sẽ nhanh hơn bằng cách sử dụng một ngoại lệ và thậm chí nó có thể nhỏ hơn.
Mike Layton

1

Cách tiếp cận thứ hai là một cách tốt.

Nếu bạn không muốn hiển thị lỗi và gây nhầm lẫn cho người dùng ứng dụng bằng cách hiển thị ngoại lệ thời gian chạy (nghĩa là lỗi) không liên quan đến họ, thì chỉ cần đăng nhập lỗi và nhóm kỹ thuật có thể tìm kiếm sự cố và giải quyết nó.

try
{
  //do some work
}
catch(Exception exception)
{
   WriteException2LogFile(exception);//it will write the or log the error in a text file
}

Tôi khuyên bạn nên tiếp cận cách thứ hai cho toàn bộ ứng dụng của mình.


2
Cách tiếp cận thứ hai không hiển thị cho người dùng hơn là đã xảy ra lỗi - ví dụ nếu họ đang lưu thứ gì đó họ sẽ không biết rằng nó đã thất bại. catchcác khối phải luôn luôn gọi throwbong bóng ngoại lệ lên hoặc trả lại một cái gì đó / hiển thị một cái gì đó cho người dùng biết rằng hành động đã thất bại. Bạn muốn nhận được cuộc gọi hỗ trợ khi họ không lưu được bất cứ thứ gì, không phải 6 tháng sau khi họ cố gắng lấy lại và không thể tìm thấy nó.
Keith

0

Để trống khối bắt là điều tồi tệ hơn để làm. Nếu có lỗi, cách tốt nhất để xử lý là:

  1. Đăng nhập vào tập tin \ cơ sở dữ liệu, v.v.
  2. Cố gắng sửa nó ngay lập tức (có thể thử cách khác để thực hiện thao tác đó)
  3. Nếu chúng tôi không thể khắc phục điều đó, hãy thông báo cho người dùng rằng có một số lỗi và tất nhiên hủy bỏ thao tác

0

Đối với tôi, xử lý ngoại lệ có thể được coi là quy tắc kinh doanh. Rõ ràng, cách tiếp cận đầu tiên là không thể chấp nhận. Cách thứ hai là tốt hơn và nó có thể đúng 100% NẾU bối cảnh nói như vậy. Bây giờ, ví dụ, bạn đang phát triển một Addin Outlook. Nếu bạn addin ném ngoại lệ chưa được xử lý, người dùng triển vọng có thể biết điều đó vì triển vọng sẽ không tự hủy vì một plugin không thành công. Và bạn có thời gian khó khăn để tìm ra những gì đã sai. Do đó, cách tiếp cận thứ hai trong trường hợp này, với tôi, đó là một cách chính xác. Bên cạnh việc ghi lại ngoại lệ, bạn có thể quyết định hiển thị thông báo lỗi cho người dùng - tôi coi đó là quy tắc kinh doanh.


0

Thực hành tốt nhất là ném Ngoại lệ khi xảy ra lỗi. Bởi vì một lỗi đã xảy ra và nó không nên được ẩn đi.

Nhưng trong cuộc sống thực, bạn có thể có một vài tình huống khi bạn muốn che giấu điều này

  1. Bạn dựa vào thành phần bên thứ ba và bạn muốn tiếp tục chương trình trong trường hợp có lỗi.
  2. Bạn có một trường hợp kinh doanh mà bạn cần tiếp tục trong trường hợp có lỗi

6
Số Đừng không ném Exception. Không bao giờ. Ném một lớp con thích hợp của Exceptiontất cả những gì bạn muốn, nhưng không bao giờ Exceptionvì điều đó hoàn toàn không có thông tin ngữ nghĩa. Tôi không thể nhìn thấy một kịch bản mà nó có ý nghĩa để ném Exceptionnhưng không phải là một lớp con của nó.
một CVn


0

Không catchcó bất kỳ đối số chỉ đơn giản là ăn ngoại lệ và không có ích. Điều gì xảy ra nếu một lỗi nghiêm trọng xảy ra? Không có cách nào để biết những gì đã xảy ra nếu bạn sử dụng bắt mà không cần tranh luận.

Một tuyên bố bắt nên bắt hơn cụ thể ngoại lệ như FileNotFoundExceptionvà sau đó tại rất kết thúc , bạn nên nắm bắt Exceptionmà sẽ bắt bất kỳ ngoại lệ khác và đăng nhập cho họ.


Tại sao có một vị tướng catch(Exception)ở cuối? Nếu bạn không mong đợi nó thì đó luôn là cách tốt nhất để chuyển nó sang lớp tiếp theo.
Keith

1
@ Vâng, vâng, bạn đúng ... không có lý do gì để bắt ngoại lệ mà bạn không mong đợi nhưng bạn có thể ngoại lệ chung cho mục đích đăng nhập ..
Anirudha

0

Đôi khi bạn cần xử lý các trường hợp ngoại lệ không nói gì với người dùng.

Cách của tôi là:

  • Để nắm bắt các ngoại lệ chưa được kiểm chứng ở cấp ứng dụng (ví dụ: trong global.asax) cho các ngoại lệ quan trọng (ứng dụng không thể hữu ích). Những ngoại lệ này tôi không nắm bắt được tại chỗ. Chỉ cần đăng nhập chúng ở cấp ứng dụng và để hệ thống thực hiện công việc của nó.
  • Bắt "tại chỗ" và hiển thị một số thông tin hữu ích cho người dùng (nhập sai số, không thể phân tích cú pháp).
  • Nắm bắt và không làm gì với các vấn đề ngoài lề như "Tôi sẽ kiểm tra thông tin cập nhật trên nền, nhưng dịch vụ không chạy".

Nó chắc chắn không phải là thực hành tốt nhất. ;-)


0

Tôi có thể nói với bạn vài điều:

Đoạn trích số 1 không được chấp nhận vì nó bỏ qua ngoại lệ. (nó nuốt nó như không có gì xảy ra).

Vì vậy, không thêm khối bắt mà không làm gì hoặc chỉ rút lại.

Bắt khối nên thêm một số giá trị. Ví dụ thông báo đầu ra cho người dùng cuối hoặc lỗi đăng nhập.

Không sử dụng ngoại lệ cho logic chương trình dòng chảy thông thường. Ví dụ:

ví dụ xác nhận đầu vào. <- Đây không phải là tình huống ngoại lệ hợp lệ, thay vào đó bạn nên viết phương thức IsValid(myInput);để kiểm tra xem mục đầu vào có hợp lệ hay không.

Mã thiết kế để tránh ngoại lệ. Ví dụ:

int Parse(string input);

Nếu chúng ta chuyển giá trị không thể được phân tích cú pháp sang int, phương thức này sẽ ném và ngoại lệ, thay vì chúng ta có thể viết một cái gì đó như thế này:

bool TryParse(string input,out int result); <- phương thức này sẽ trả về boolean cho biết nếu phân tích thành công.

Có thể đây là một chút ngoài phạm vi của câu hỏi này, nhưng tôi hy vọng điều này sẽ giúp bạn đưa ra quyết định đúng đắn khi nó xảy ra try {} catch(){}và ngoại lệ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.