Tại sao các khối bắt trống là một ý tưởng tồi? [đóng cửa]


192

Tôi vừa thấy một câu hỏi về thử bắt , mà mọi người (bao gồm cả Jon Skeet) nói rằng các khối bắt trống là một ý tưởng thực sự tồi? Tại sao là cái này? Không có tình huống mà một sản phẩm khai thác trống không phải là một quyết định thiết kế sai?

Ý tôi là, chẳng hạn, đôi khi bạn muốn nhận thêm một số thông tin từ đâu đó (dịch vụ web, cơ sở dữ liệu) và bạn thực sự không quan tâm liệu bạn có nhận được thông tin này hay không. Vì vậy, bạn cố gắng để có được nó, và nếu có bất cứ điều gì xảy ra, điều đó ổn, tôi sẽ chỉ thêm một "bắt (Ngoại lệ bị bỏ qua) {}" và đó là tất cả


53
Tôi sẽ viết một bình luận giải thích tại sao nó nên trống rỗng.
Mehrdad Afshari

5
... Hoặc ít nhất là đăng nhập rằng nó đã bị bắt.
matt b

2
@ocdecio: tránh nó để mã dọn dẹp , không tránh nó nói chung.
John Saunders

1
@ocdecio: Một trường hợp khác để tránh sử dụng try..catch là khi bạn bắt ngoại lệ cho các loại lỗi đã biết. ví dụ: ngoại lệ chuyển đổi số
MPritchard

1
Tài liệu thất bại thầm lặng ".
Nate

Câu trả lời:


297

Thông thường, việc thử bắt trống là một ý tưởng tồi vì bạn đang âm thầm nuốt một điều kiện lỗi và sau đó tiếp tục thực hiện. Đôi khi, đây có thể là điều đúng đắn, nhưng thường đó là dấu hiệu cho thấy nhà phát triển đã thấy một ngoại lệ, không biết phải làm gì về vấn đề đó và vì vậy đã sử dụng một biện pháp bắt trống để làm im lặng vấn đề.

Đó là chương trình tương đương với việc dán băng keo đen lên đèn cảnh báo động cơ.

Tôi tin rằng cách bạn xử lý các trường hợp ngoại lệ phụ thuộc vào lớp phần mềm bạn đang làm việc: Ngoại lệ trong Rainforest .


4
Trong những trường hợp thực sự hiếm hoi mà tôi thực sự không cần ngoại lệ vì lý do nào đó tôi không thể đăng nhập, tôi chắc chắn nhận xét rằng đó là cố ý - và tại sao không cần thiết, và nhắc lại rằng tôi vẫn thích đăng nhập nếu nó khả thi trong môi trường đó Về cơ bản, tôi đưa ra nhận xét THÊM công việc hơn là đăng nhập sẽ là một lựa chọn trong hoàn cảnh đó. May mắn thay, tôi có 2 khách hàng, đây là một vấn đề và chỉ khi gửi e-mail không quan trọng từ trang web của họ.
John Rudy

37
Cũng thích sự tương tự - và giống như tất cả các quy tắc, có những trường hợp ngoại lệ. Chẳng hạn, hoàn toàn ổn khi sử dụng băng đen trên đồng hồ nhấp nháy trên VCR của bạn nếu bạn không bao giờ có ý định thực hiện ghi âm theo thời gian (đối với tất cả những người dân xưa của bạn, những người nhớ VCR là gì).
Michael Burr

3
Giống như bất kỳ sự khởi hành nào từ thực tiễn được chấp nhận, hãy làm điều đó nếu thích hợp và ghi lại nó để chàng trai tiếp theo biết bạn đã làm gì và tại sao.
David Thornley

5
@Jason: ít nhất, bạn nên bao gồm một nhận xét chi tiết giải thích lý do tại sao bạn im lặng ngoại lệ.
Ned Batchelder

1
Câu trả lời này giả định hai điều: 1. Ngoại lệ bị ném thực sự là một ý nghĩa và người viết mã ném ra biết những gì anh ta đang làm; 2. Ngoại lệ đó thực sự là một "điều kiện lỗi" và không bị lạm dụng như luồng điều khiển (mặc dù đây là trường hợp cụ thể hơn về điểm đầu tiên của tôi).
Boris B.

37

Nói chung, chúng là một ý tưởng tồi bởi vì đó là một điều kiện thực sự hiếm gặp khi một thất bại (điều kiện đặc biệt, tổng quát hơn) được đáp ứng đúng với KHÔNG có phản ứng gì. Trên hết, catchcác khối trống là một công cụ phổ biến được sử dụng bởi những người sử dụng công cụ ngoại lệ để kiểm tra lỗi rằng họ nên thực hiện một cách ưu tiên.

Phải nói rằng điều đó luôn tệ là không đúng sự thật ... điều đó đúng với rất ít. Có thể có trường hợp bạn không quan tâm rằng có lỗi hoặc sự hiện diện của lỗi bằng cách nào đó cho thấy rằng bạn không thể làm gì về lỗi này (ví dụ: khi viết lỗi trước đó vào tệp nhật ký văn bản và bạn nhận được một IOException, có nghĩa là bạn không thể viết ra lỗi mới nào).


11

Tôi sẽ không nói quá nhiều rằng người sử dụng các khối bắt trống là một lập trình viên tồi và không biết anh ta đang làm gì ...

Tôi sử dụng khối bắt trống nếu cần thiết. Đôi khi lập trình viên của thư viện tôi đang tiêu thụ không biết anh ta đang làm gì và ném ngoại lệ ngay cả trong những tình huống khi không ai cần nó.

Ví dụ: hãy xem xét một số thư viện máy chủ http, tôi không quan tâm nếu máy chủ ném ngoại lệ vì máy khách bị ngắt kết nối và index.htmlkhông thể gửi được.


6
Bạn chắc chắn là quá khái quát. Chỉ vì bạn không cần nó không có nghĩa là không có ai. Ngay cả khi hoàn toàn không có gì có thể được thực hiện để đáp lại, vẫn có ai đó yêu cầu thu thập số liệu thống kê về các kết nối bị bỏ rơi. Vì vậy, thật thô lỗ khi cho rằng "lập trình viên của thư viện không biết anh ta đang làm gì".
Ben Voigt

8

Có những trường hợp hiếm hoi mà nó có thể được biện minh. Trong Python bạn thường thấy kiểu xây dựng này:

try:
    result = foo()
except ValueError:
    result = None

Vì vậy, nó có thể ổn (tùy thuộc vào ứng dụng của bạn) để làm:

result = bar()
if result == None:
    try:
        result = foo()
    except ValueError:
        pass # Python pass is equivalent to { } in curly-brace languages
 # Now result == None if bar() returned None *and* foo() failed

Trong một dự án .NET gần đây, tôi đã phải viết mã để liệt kê các DLL plugin để tìm các lớp thực hiện một giao diện nhất định. Mã bit có liên quan (trong VB.NET, xin lỗi) là:

    For Each dllFile As String In dllFiles
        Try
            ' Try to load the DLL as a .NET Assembly
            Dim dll As Assembly = Assembly.LoadFile(dllFile)
            ' Loop through the classes in the DLL
            For Each cls As Type In dll.GetExportedTypes()
                ' Does this class implement the interface?
                If interfaceType.IsAssignableFrom(cls) Then

                    ' ... more code here ...

                End If
            Next
        Catch ex As Exception
            ' Unable to load the Assembly or enumerate types -- just ignore
        End Try
    Next

Mặc dù trong trường hợp này, tôi thừa nhận rằng việc ghi lại sự thất bại ở đâu đó có lẽ sẽ là một sự cải thiện.


Tôi đã gọi log4net là một trong những trường hợp đó trước khi tôi thấy câu trả lời của bạn.
Scott Lawrence

7

Các khối bắt trống thường được đưa vào vì bộ mã hóa không thực sự biết họ đang làm gì. Tại tổ chức của tôi, một khối bắt trống phải bao gồm một nhận xét về lý do tại sao không làm gì với ngoại lệ là một ý tưởng tốt.

Trên một lưu ý liên quan, hầu hết mọi người không biết rằng một khối thử {} có thể được theo dõi bằng một lệnh bắt {} hoặc cuối cùng {}, chỉ cần một khối.


1
+1 Tôi nhấn mạnh 'hoặc' trong câu cuối cùng đó để thêm tác động
MPritchard

Đoạn thứ hai đó có thể phụ thuộc vào ngôn ngữ, tuy nhiên, phải không?
David Z

3
tất nhiên trừ khi bạn đang nói về IE6 hoặc IE7 ;-) ... trong trường hợp bắt IS bắt buộc hoặc cuối cùng không thực thi. (điều này đã được sửa trong IE8 btw)
scunliffe


Rất đúng. Có thể có giá trị làm nổi bật các ngôn ngữ. Tôi tin rằng .net vẫn ổn
MPritchard

7

Các ngoại lệ chỉ nên được ném nếu thực sự có ngoại lệ - một cái gì đó xảy ra vượt quá tiêu chuẩn. Một khối bắt trống về cơ bản nói rằng "một cái gì đó xấu đang xảy ra, nhưng tôi không quan tâm". Đây là một ý tưởng tồi.

Nếu bạn không muốn xử lý ngoại lệ, hãy để nó lan truyền lên trên cho đến khi nó đạt đến một số mã có thể xử lý nó. Nếu không có gì có thể xử lý ngoại lệ, nó sẽ đưa ứng dụng xuống.


3
Đôi khi bạn biết rằng nó sẽ không ảnh hưởng đến bất cứ điều gì. Sau đó hãy tiếp tục và thực hiện nó, và nhận xét nó để anh chàng tiếp theo không nghĩ rằng bạn sai lầm vì bạn không đủ năng lực.
David Thornley

Vâng, có một thời gian và một nơi cho tất cả mọi thứ. Tuy nhiên, nói chung, tôi nghĩ rằng một khối bắt trống tốt là ngoại lệ của quy tắc - đó là một trường hợp hiếm hoi mà bạn muốn thực sự bỏ qua một ngoại lệ (thông thường, IMO, đó là kết quả của việc sử dụng ngoại lệ kém).
Sậy Copsey

2
@David Thornley: Làm thế nào để bạn biết nó sẽ không ảnh hưởng đến bất cứ điều gì nếu bạn ít nhất kiểm tra ngoại lệ để xác định xem đó có phải là một lỗi dự kiến ​​và vô nghĩa hay là một lỗi nên được lan truyền lên trên?
Dave Sherohman

2
@Dave: Mặc dù catch (Exception) {}là một ý tưởng tồi, nhưng catch (SpecificExceptionType) {}có thể hoàn toàn ổn. Lập trình viên DID kiểm tra ngoại lệ, sử dụng thông tin loại trong mệnh đề bắt.
Ben Voigt

7

Tôi nghĩ rằng nó ổn nếu bạn bắt loại ngoại lệ cụ thể mà bạn biết rằng nó sẽ chỉ được nêu ra vì một lý do cụ thể và bạn mong đợi ngoại lệ đó và thực sự không cần phải làm gì về nó.

Nhưng ngay cả trong trường hợp đó, một thông báo gỡ lỗi có thể theo thứ tự.


5

Per Josh Bloch - Mục 65: Đừng bỏ qua Ngoại lệ của Java hiệu quả :

  1. Một khối bắt trống đánh bại mục đích của ngoại lệ
  2. Ít nhất, khối bắt phải chứa một bình luận giải thích lý do tại sao nó phù hợp để bỏ qua ngoại lệ.

3

Một khối bắt trống về cơ bản là nói rằng "Tôi không muốn biết lỗi nào được ném, tôi sẽ bỏ qua chúng."

Nó tương tự như VB6 On Error Resume Next, ngoại trừ mọi thứ trong khối thử sau khi ném ngoại lệ sẽ bị bỏ qua.

Mà không giúp được gì khi cái gì đó sau đó phá vỡ.


Tôi thực sự nói "On Error Resume Next" tệ hơn - nó sẽ chỉ thử dòng tiếp theo bất kể. Một sản phẩm khai thác trống sẽ nhảy để vượt qua giai đoạn kết thúc của tuyên bố thử
MPritchard

Điểm tốt. Tôi có lẽ nên lưu ý rằng trong phản ứng của tôi.
Powerlord

1
Điều này không hoàn toàn chính xác, nếu bạn có một thử trống ... bắt với một loại ngoại lệ cụ thể, thì nó sẽ bỏ qua loại ngoại lệ này và điều đó có thể hợp pháp ...
Meta-Knight

Nhưng nếu bạn biết một loại ngoại lệ cụ thể đang bị ném, có vẻ như bạn có thể có đủ thông tin để viết logic của mình theo cách tránh sử dụng thử bắt để xử lý tình huống.
Scott Lawrence

@Scott: Một số ngôn ngữ (ví dụ Java) đã kiểm tra các ngoại lệ ... ngoại lệ bạn buộc phải viết các khối bắt cho.
Powerlord

3

Điều này đi đôi với "Không sử dụng ngoại lệ để kiểm soát luồng chương trình." Và "Chỉ sử dụng ngoại lệ cho các trường hợp đặc biệt." Nếu những điều này được thực hiện, thì ngoại lệ chỉ nên xảy ra khi có vấn đề. Và nếu có vấn đề, bạn không muốn thất bại trong âm thầm. Trong các trường hợp dị thường hiếm khi không cần thiết phải xử lý sự cố, ít nhất bạn nên ghi lại ngoại lệ, chỉ trong trường hợp dị thường không còn là bất thường nữa. Điều duy nhất tồi tệ hơn thất bại là thất bại âm thầm.


2

Tôi nghĩ rằng một khối bắt hoàn toàn trống là một ý tưởng tồi bởi vì không có cách nào để suy luận rằng bỏ qua ngoại lệ là hành vi dự định của mã. Không nhất thiết là xấu khi nuốt một ngoại lệ và trả về false hoặc null hoặc một số giá trị khác trong một số trường hợp. Khung .net có nhiều phương thức "thử" hoạt động theo cách này. Theo nguyên tắc thông thường nếu bạn nuốt một ngoại lệ, hãy thêm nhận xét và tuyên bố nhật ký nếu ứng dụng hỗ trợ đăng nhập.


1

Bởi vì nếu một ngoại lệ được ném bạn sẽ không bao giờ nhìn thấy nó - không âm thầm là lựa chọn tồi tệ nhất có thể - bạn sẽ nhận được hành vi sai lầm và không có ý tưởng để nhìn nơi nó đang xảy ra. Ít nhất là đặt một thông điệp tường trình ở đó! Ngay cả khi đó là điều 'không bao giờ có thể xảy ra'!


1

Các khối bắt trống là một dấu hiệu của một lập trình viên không biết phải làm gì với một ngoại lệ. Họ đang ngăn chặn ngoại lệ có thể nổi lên và được xử lý chính xác bởi một khối thử khác. Luôn luôn cố gắng và làm một cái gì đó với ngoại lệ bạn đang nắm bắt.


1

Tôi thấy khó chịu nhất với các câu lệnh bắt trống là khi một số lập trình viên khác đã làm điều đó. Ý tôi là khi bạn cần gỡ lỗi mã từ người khác, bất kỳ câu lệnh bắt trống nào làm cho việc thực hiện như vậy trở nên khó khăn hơn. Các câu lệnh bắt IMHO phải luôn hiển thị một số loại thông báo lỗi - ngay cả khi lỗi không được xử lý, ít nhất nó cũng phải phát hiện ra (chỉ bật trong chế độ gỡ lỗi)


1

Có lẽ điều đó không bao giờ đúng vì bạn đang âm thầm vượt qua mọi ngoại lệ có thể. Nếu có một ngoại lệ cụ thể mà bạn mong đợi, thì bạn nên kiểm tra nó, suy nghĩ lại nếu đó không phải là ngoại lệ của bạn.

try
{
    // Do some processing.
}
catch (FileNotFound fnf)
{
    HandleFileNotFound(fnf);
}
catch (Exception e)
{
    if (!IsGenericButExpected(e))
        throw;
}

public bool IsGenericButExpected(Exception exception)
{
    var expected = false;
    if (exception.Message == "some expected message")
    {
        // Handle gracefully ... ie. log or something.
        expected = true;
    }

    return expected;
}

1
Đó thực sự không phải là một mẫu chuẩn mà bạn khuyên mọi người nên sử dụng ở đó. Hầu hết mọi người sẽ chỉ nắm bắt Ngoại lệ của bất kỳ loại nào họ đang mong đợi, và không phải loại nào họ không mong muốn. Tôi có thể thấy một số trường hợp cách tiếp cận của bạn sẽ hợp lệ, chỉ cần cẩn thận đăng bài trên một diễn đàn như thế này nơi mọi người có xu hướng đọc những thứ như thế này vì họ không hiểu ngay từ đầu;)
MPritchard

Ý định của tôi không phải là IsKnownException () đang kiểm tra loại ngoại lệ (vâng, bạn nên làm điều đó bằng các khối bắt khác nhau), thay vào đó là kiểm tra xem đó có phải là ngoại lệ dự kiến ​​không. Tôi sẽ chỉnh sửa để làm cho nó rõ ràng hơn.
xanadont

Ban đầu tôi đã nghĩ về COMExceptions. Bạn có thể kiểm tra một Mã lỗi cụ thể và xử lý các mã bạn mong đợi. Tôi làm điều này thường xuyên khi lập trình với ArcOjbects.
xanadont

2
-1 Đưa ra quyết định trong mã dựa trên thông báo ngoại lệ là một ý tưởng thực sự tồi tệ. Thông điệp chính xác hiếm khi được ghi lại và có thể thay đổi bất cứ lúc nào; đó là một chi tiết thực hiện. Hoặc thông điệp có thể dựa trên chuỗi tài nguyên có thể bản địa hóa. Trong mọi trường hợp, nó chỉ có nghĩa là được đọc bởi một con người.
Wim Coenen

Eek. Ngoại lệ.Message có thể được bản địa hóa và do đó không phải là những gì bạn mong đợi. Điều này chỉ sai.
thân

1

Nói chung, bạn chỉ nên nắm bắt các ngoại lệ mà bạn thực sự có thể xử lý. Điều đó có nghĩa là càng cụ thể càng tốt khi bắt ngoại lệ. Nắm bắt tất cả các ngoại lệ hiếm khi là một ý tưởng tốt và bỏ qua tất cả các ngoại lệ gần như luôn luôn là một ý tưởng rất xấu.

Tôi chỉ có thể nghĩ về một vài trường hợp trong đó một khối bắt trống có mục đích nào đó có ý nghĩa. Nếu bất kỳ ngoại lệ cụ thể nào, bạn đang bắt được "xử lý" bằng cách chỉ đánh lừa hành động thì sẽ không cần phải làm gì trong khối bắt. Tuy nhiên, vẫn sẽ là một ý tưởng tốt để ghi lại sự thật rằng ngoại lệ xảy ra.

Một ví dụ khác: CLR 2.0 đã thay đổi cách xử lý các ngoại lệ chưa xử lý trên luồng hoàn thiện. Trước 2.0, quy trình được phép tồn tại trong kịch bản này. Trong CLR hiện tại, quá trình được kết thúc trong trường hợp ngoại lệ chưa được xử lý trên luồng hoàn thiện.

Hãy nhớ rằng bạn chỉ nên thực hiện một bộ hoàn thiện nếu bạn thực sự cần một cái và thậm chí sau đó bạn nên làm một chút có thể trong bộ hoàn thiện. Nhưng nếu bất cứ công việc nào mà trình hoàn thiện của bạn phải làm có thể tạo ra một ngoại lệ, bạn cần phải chọn giữa hai tệ nạn nhỏ hơn. Bạn có muốn tắt ứng dụng do ngoại lệ chưa xử lý? Hay bạn muốn tiến hành trong một trạng thái ít nhiều không xác định? Ít nhất trong lý thuyết, cái sau có thể là ít hơn của hai tệ nạn trong một số trường hợp. Trong những trường hợp đó, khối bắt trống sẽ ngăn quá trình bị chấm dứt.


1
Ý tôi là, chẳng hạn, đôi khi bạn muốn nhận thêm một số thông tin từ đâu đó (dịch vụ web, cơ sở dữ liệu) và bạn thực sự không quan tâm liệu bạn có nhận được thông tin này hay không. Vì vậy, bạn cố gắng để có được nó, và nếu có bất cứ điều gì xảy ra, điều đó ổn, tôi sẽ chỉ thêm một "bắt (Ngoại lệ bị bỏ qua) {}" và đó là tất cả

Vì vậy, đi theo ví dụ của bạn, đó là một ý tưởng tồi trong trường hợp đó bởi vì bạn đang nắm bắt và bỏ qua tất cả các ngoại lệ. Nếu bạn chỉ bắt EInfoFromIrrelevantSourceNotAvailablevà bỏ qua nó, điều đó sẽ ổn, nhưng bạn thì không. Bạn cũng đang bỏ qua ENetworkIsDown, điều này có thể hoặc không quan trọng. Bạn đang phớt lờ ENetworkCardHasMeltedEFPUHasDecidedThatOnePlusOneIsSeventeen , điều gần như chắc chắn quan trọng.

Khối bắt trống không phải là vấn đề nếu nó được thiết lập để chỉ bắt (và bỏ qua) các ngoại lệ của một số loại nhất định mà bạn biết là không quan trọng. Các tình huống trong đó là một ý tưởng tốt để ngăn chặn và âm thầm bỏ qua tất cả các ngoại lệ, mà không dừng lại để kiểm tra chúng trước để xem liệu chúng có được mong đợi / bình thường / không liên quan hay không, là cực kỳ hiếm.


1

Có những tình huống bạn có thể sử dụng chúng, nhưng chúng sẽ không thường xuyên. Các tình huống mà tôi có thể sử dụng một bao gồm:

  • khai thác ngoại lệ; tùy thuộc vào ngữ cảnh, bạn có thể muốn có một ngoại lệ hoặc tin nhắn chưa được xử lý được đăng thay thế.

  • lặp lại các tình huống kỹ thuật, như kết xuất hoặc xử lý âm thanh hoặc gọi lại hộp danh sách, trong đó chính hành vi đó sẽ giải quyết vấn đề, đưa ra một ngoại lệ sẽ gây cản trở và ghi nhật ký ngoại lệ có thể sẽ dẫn đến 1000 tin nhắn "không thành công" .

  • các chương trình không thể thất bại, mặc dù ít nhất họ vẫn nên đăng nhập một cái gì đó.

đối với hầu hết các ứng dụng winforms, tôi đã thấy rằng nó đủ để có một tuyên bố thử duy nhất cho mỗi đầu vào của người dùng. Tôi sử dụng các phương thức sau: (AlertBox chỉ là một trình bao bọc MessageBox.Show nhanh)

  public static bool TryAction(Action pAction)
  {
     try { pAction(); return true; }
     catch (Exception exception)
     {
        LogException(exception);
        return false;
     }
  }

  public static bool TryActionQuietly(Action pAction)
  {
     try { pAction(); return true; }
     catch(Exception exception)
     {
        LogExceptionQuietly(exception);
        return false;
     }
  }

  public static void LogException(Exception pException)
  {
     try
     {
        AlertBox(pException, true);
        LogExceptionQuietly(pException);
     }
     catch { }
  }

  public static void LogExceptionQuietly(Exception pException)
  {
     try { Debug.WriteLine("Exception: {0}", pException.Message); } catch { }
  }

Sau đó, mọi trình xử lý sự kiện có thể làm một cái gì đó như:

  private void mCloseToolStripMenuItem_Click(object pSender, EventArgs pEventArgs)
  {
     EditorDefines.TryAction(Dispose);
  }

hoặc là

  private void MainForm_Paint(object pSender, PaintEventArgs pEventArgs)
  {
     EditorDefines.TryActionQuietly(() => Render(pEventArgs));
  }

Về mặt lý thuyết, bạn có thể có TryActionSilently, có thể tốt hơn để kết xuất các cuộc gọi để một ngoại lệ không tạo ra vô số tin nhắn.


1

Nếu bạn không biết phải làm gì trong khối bắt, bạn chỉ có thể đăng nhập ngoại lệ này, nhưng không để trống.

        try
        {
            string a = "125";
            int b = int.Parse(a);
        }
        catch (Exception ex)
        {
            Log.LogError(ex);
        }

Tại sao điều này bị hạ cấp?
Vino

0

Bạn không bao giờ nên có một khối bắt trống. Nó giống như che giấu một sai lầm mà bạn biết. Ít nhất bạn nên viết ra một ngoại lệ đối với tệp nhật ký để xem lại sau nếu bạn bị ép thời gian.

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.