Có bao giờ ok để có một tuyên bố bắt trống?


58

Tôi nghĩ về nó và không thể đưa ra một ví dụ. Tại sao ai đó muốn bắt một ngoại lệ và không làm gì về nó? Bạn có thể đưa ra một ví dụ không? Có lẽ nó chỉ là thứ không bao giờ nên làm.


Cuối cùng thì sao?
Chris

2
btw - điều này đã được hỏi trên SO năm ngoái: stackoverflow.com/questions/1234343/ trên
warren

17
ít nhất nó nên chứa một bình luận tại sao nó trống rỗng.
peterchen

Câu trả lời:


77

Tôi làm điều đó mọi lúc với những thứ như lỗi chuyển đổi trong D:

import std.conv, std.stdio, std.exception;

void main(string[] args) {  
    enforce(args.length > 1, "Usage:  foo.exe filename");

    double[] nums;

    // Process a text file with one number per line into an array of doubles,
    // ignoring any malformed lines.
    foreach(line; File(args[1]).byLine()) {
        try {
            nums ~= to!double(line);
        } catch(ConvError) {
            // Ignore malformed lines.
        }
    }

    // Do stuff with nums.
}

Điều đó nói rằng, tôi nghĩ rằng tất cả các khối bắt phải có một cái gì đó trong chúng, ngay cả khi đó chỉ là một nhận xét giải thích lý do tại sao bạn bỏ qua ngoại lệ.

Chỉnh sửa: Tôi cũng muốn nhấn mạnh rằng, nếu bạn định làm điều gì đó như thế này, bạn nên cẩn thận chỉ bắt ngoại lệ cụ thể mà bạn muốn bỏ qua. Làm một người già đơn giản catch {}hầu như luôn luôn là một điều xấu.


15
+1 để nhận xét giải thích.
Michael K

Một số IDE cho phép bạn chỉ định rằng một tên cụ thể cho ngoại lệ (thường là "bỏ qua") biểu thị rằng bạn đang cố tình phớt lờ nó, điều này sẽ ngăn chặn cảnh báo mà nó sẽ hiển thị; Điều này có vị trí của bình luận.
Alex Feinman

Tôi không quen thuộc với D, nhưng tôi cho rằng có thể làm một cái gì đó như bool TryPude (dòng chuỗi, ra hai valToPude), có thể sạch hơn.
ysolik

4
Wow, một người thực sự sử dụng D! :-P
James McNellis

4
Giống như @Michael nói - nó không hoàn toàn trống rỗng vì bạn đã nhận xét ở đó; bắt buộc phải thêm nhận xét "đánh bắt này cố ý để trống" nếu không có tuyên bố
STW

50

Một ví dụ mà tôi nghĩ là ổn khi chỉ nuốt ngoại lệ mà không làm gì cả, thậm chí ghi nhật ký ngoại lệ, nằm trong chính mã đăng nhập.

Nếu bạn đã cố gắng đăng nhập một cái gì đó và có ngoại lệ, bạn không thể làm gì nhiều về nó:

  • bạn không thể đăng nhập nó tất nhiên;
  • bạn có thể quay lại cơ chế ghi nhật ký dự trữ, nhưng hầu hết các ứng dụng không phức tạp và đối với những ứng dụng đủ tinh vi, vấn đề thiết kế tương tự sẽ phát sinh với cơ chế ghi nhật ký dự trữ;
  • thất bại nhanh - sẽ là một giải pháp quá triệt để cho hầu hết các ứng dụng;
  • ném ngoại lệ lên sẽ làm rất ít vì nó sẽ kết thúc trong một câu lệnh bắt lên ngăn xếp mà gần như chắc chắn sẽ cố gắng đăng nhập nó ...

Điều đó khiến bạn có tùy chọn "ít nhất là xấu xa" khi lặng lẽ nuốt nó, cho phép ứng dụng tiếp tục thực hiện công việc chính của mình, nhưng ảnh hưởng đến khả năng khắc phục sự cố.


10
điểm tuyệt vời. gỡ lỗi mã gỡ lỗi luôn là một thách thức.
DevSolo

7
Điều này, một triệu lần. Đặc biệt là khi bạn xem xét rằng nếu bạn đăng nhập, bạn có thể ở trong tình trạng tồi tệ; bạn có thể hết bộ nhớ, có thể bị rơi, có thể là bất cứ điều gì. Tại thời điểm này, khi bạn đang đăng nhập một lỗi và THAT thất bại, điều duy nhất có trách nhiệm phải làm là không có gì; bạn không có gì đảm bảo rằng bất cứ điều gì bạn thử sẽ không làm cho nó tồi tệ hơn.
GWLlosa

Điểm tuyệt vời. Trong mã đăng nhập của mình, tôi cũng muốn thêm InternalMessage cho ngoại lệ. Nhiều lần điều này là không có giá trị, vì vậy cố gắng thêm nó sẽ làm nổ tung bản ghi.
Tangurena

@Tangurena Nếu nó thường không có thì hãy xử lý nó, đừng thổi. Trong C # tôi thường bảo vệ những thứ như vậy bằng cách ?? "";
Loren Pechtel

8

Nếu một ngoại lệ được ném bởi một thao tác là tùy chọn, thì có thể không có gì để làm. Nó có thể không hữu ích để đăng nhập nó. Trong trường hợp đó, bạn có thể chỉ muốn bắt nó và không làm gì cả.

Nếu bạn đang làm điều này, bao gồm một nhận xét. Luôn bình luận bất cứ điều gì trông giống như một lỗi (lỗi trong một switchcâu lệnh, catchkhối trống , bài tập trong một điều kiện).

Bạn có thể lập luận rằng đây không phải là cách sử dụng ngoại lệ thích hợp, nhưng đó là một câu hỏi khác.


6

catchKhối trống là một mùi mã trong hầu hết các ngôn ngữ. Ý tưởng chính là sử dụng các ngoại lệ cho các tình huống đặc biệt và không sử dụng chúng để kiểm soát logic. Tất cả các ngoại lệ phải được xử lý ở đâu đó.

Do hậu quả của việc thực hiện phương pháp này, khi bạn lập trình một lớp ứng dụng cụ thể, bạn có một số lựa chọn:

  • không làm gì về ngoại lệ. Nó sẽ bị bắt và xử lý bởi một lớp khác
  • bắt nó và thực hiện các hành động khắc phục.
  • bắt nó, làm một cái gì đó, nhưng ném lại cho một lớp khác để xử lý

Điều này không thực sự để lại bất kỳ phòng cho không làm gì, catchkhối trống .

EDITED TO ADD : giả sử bạn đang lập trình bằng một ngôn ngữ trong đó ném ngoại lệ là cách thông thường để kiểm soát logic chương trình (một trong những lựa chọn thay thế goto). Sau đó, một catchkhối trống trong một chương trình được viết bằng ngôn ngữ này rất giống với một elsekhối trống trong ngôn ngữ truyền thống (hoặc không có elsekhối nào cả). Tuy nhiên, tôi tin rằng đây không phải là phong cách lập trình được đề xuất bởi các cộng đồng phát triển C #, Java hoặc C ++.


Tôi nghĩ rằng việc sử dụng các ngoại lệ làm điều khiển logic đã trở nên khá phổ biến, vì vậy tôi thấy mình có các khối bắt trống hoặc gần như trống rỗng khá thường xuyên.
whatsisname

+1 để nhận ra rằng một khối bắt trống có thể chỉ ra việc sử dụng các ngoại lệ để kiểm soát luồng.
John M Gant

Sử dụng ngoại lệ cho logic lập trình thường được coi là thực hành xấu. Nhưng, đáng ngạc nhiên (lập luận ban đầu của tôi chống lại các ngoại lệ) ngoại lệ không phát sinh một hiệu suất đáng chú ý. Xem codeproject.com/KB/exception/ExceptionPerformance.aspx .
Evan Plaice

6

Trong một số trường hợp, Java yêu cầu bạn xử lý một ngoại lệ có thể, trong mọi trường hợp hoàn toàn không xảy ra. Hãy xem xét mã này:

try {
   bytes[] hw = "Hello World".getBytes("UTF-8");
}
catch(UnsupportedCodingException e) {
}

Mọi triển khai của nền tảng Java là bắt buộc để hỗ trợ các bộ ký tự chuẩn sau đây.

...

UTF-8

Không phá vỡ nền tảng Java của bạn, đơn giản là không có cách nào để gây ra ngoại lệ này. Vậy tại sao phải xử lý nó? Nhưng bạn không thể đơn giản sử dụng mệnh đề try-Catch-.


1
Trong những trường hợp như thế này, tôi sẽ cố gắng thêm throw new Error(e)chỉ để chắc chắn rằng tôi đã không bỏ lỡ bất cứ điều gì (hoặc báo cáo nền tảng thực sự bị hỏng).
Halle Knast

@HalleKnast Có. Điều này đã được thảo luận chi tiết đầy đủ ở đây: softwareengineering.stackexchange.com/questions/122233/iêu
user281377

5

Theo nguyên tắc chung, không. Bạn có thể muốn ném ngoại lệ lên ngăn xếp, hoặc ghi lại những gì đã xảy ra.

Tuy nhiên, tôi thường làm điều này khi tôi phân tích chuỗi và chuyển đổi thành số (đặc biệt là trong các dự án ánh xạ được sử dụng một lần và loại bỏ nhiều lần).

boolean isNonZeroNumber = false;

try {
   if(StringUtils.isNotBlank(s) && new BigDecimal(s).compareTo(BigDecimal.ZERO) != 0) {
     b = true;
   }
} catch(NumberFormatException e) {
//swallow this exception; b stays false.
}

return b;

3

Trong một số trường hợp, ngoại lệ hoàn toàn không phải là ngoại lệ; trong thực tế, nó được mong đợi Xem xét ví dụ này:

    /* Check if the process is still alive; exitValue() throws an exception if it is */
    try {
        p.exitValue();
        processPool.remove(p);
    }
    catch (IllegalThreadStateException e) { /* expected behaviour */ }

Vì không có phương thức isAlive () trong java.lang.Process (), nên cách duy nhất để kiểm tra xem nó có còn sống hay không là gọi exitValue (), ném IllegalThreadStateException nếu quá trình vẫn đang chạy.


2

Theo kinh nghiệm khiêm tốn của tôi, hiếm khi điều này là tốt. Bạn có thể thay đổi số dặm.

Gần như mỗi lần tôi nhìn thấy điều này trong cơ sở mã của chúng tôi, đó là vì tôi đã theo dõi một lỗi mà ngoại lệ ăn được đã ẩn. Nói cách khác, bằng cách không cung cấp bất kỳ mã nào, ứng dụng không có cách nào để chỉ ra lỗi hoặc thực hiện một hành động khác.

Đôi khi, tại các lớp API chẳng hạn, bạn có thể không muốn hoặc không thể để ngoại lệ vượt qua, vì vậy trong trường hợp đó, tôi có xu hướng thử và bắt những cái chung nhất, sau đó ghi nhật ký. Một lần nữa, ít nhất là cho một phiên gỡ lỗi / chẩn đoán một cơ hội.

Thông thường, nếu nó phải trống, ít nhất tôi làm là đưa ra một khẳng định nào đó, vì vậy ít nhất một cái gì đó xảy ra trong một phiên gỡ lỗi. Điều đó nói rằng, nếu tôi không thể xử lý nó, tôi có xu hướng bỏ qua chúng hoàn toàn.


2

IMO có một nơi mà các câu lệnh bắt trống là OK. Trong các trường hợp thử nghiệm mà bạn mong đợi một ngoại lệ:

try
{
   DoSomething(); //This should throw exception if everything works well
   Assert.Fail('Expected exception was not thrown');
}
catch(<whatever exception>)
{
    //Expected to get here
}

+1, tôi ghét việc tôi làm điều này nhưng nó hoạt động trong JUnit
sal

@sal: còn @Test (dự kiến ​​= Exception. class) thì sao?
ysolik

@ysolik, thói quen xấu
sal

1
@sal: Tại sao đó là một thói quen xấu?
Adam Lear

Ừm. có nhiều cách để làm điều này với các khung kiểm tra đơn vị. Ví dụ. Không. Có một số trường hợp trong đó kiểm tra rằng một cái gì đó dự đoán thất bại có thể hữu ích. Mặc dù, tôi sẽ không bao giờ làm điều đó trong mã sản xuất.
Evan Plaice

2

Tôi tin rằng có một số trường hợp nhất định trong đó có một điều khoản bắt trống - đây là những trường hợp khi bạn muốn mã chỉ tiếp tục nếu một hoạt động cụ thể thất bại. Tuy nhiên, tôi nghĩ rằng mô hình này có thể dễ dàng bị lạm dụng, vì vậy mỗi lần sử dụng nên được kiểm tra chặt chẽ để đảm bảo không có cách nào tốt hơn để xử lý nó. Cụ thể, điều này không bao giờ được thực hiện nếu nó khiến chương trình ở trạng thái không nhất quán hoặc nếu nó có thể dẫn đến một số phần khác của mã bị lỗi sau này.


1

Tôi không tin vào việc không có một số mức độ cảnh báo khi có ngoại lệ xảy ra - không phải một trong những mục tiêu của lập trình là cải thiện mã? Nếu một ngoại lệ xảy ra 10% thời gian và bạn không bao giờ biết về nó, thì ngoại lệ đó sẽ luôn tệ đến mức đó hoặc tệ hơn.

Tuy nhiên, tôi thấy mặt khác, rằng nếu ngoại lệ không có tác hại thực sự trong việc xử lý mã, thì tại sao lại phơi bày người dùng với nó. Giải pháp tôi thường thực hiện là ghi nhật ký bởi lớp logger của tôi (đó là trong mỗi lớp tôi từng viết tại nơi làm việc).

Nếu đó là một ngoại lệ lành tính, thì tôi thực hiện cuộc gọi đến phương thức .Debug () của Logger; mặt khác, Logger.Error () (và (có thể) ném).

try
{
   doWork();
}
catch(BenignException x)
{
  _log.Debug("Error doing work: " + x.Message);
}

Nhân tiện, khi tôi thực hiện trình ghi nhật ký của mình, thực hiện cuộc gọi đến phương thức .Debug () sẽ chỉ ghi nhật ký nếu tệp App.Config của tôi chỉ định mức nhật ký thực thi chương trình ở chế độ Gỡ lỗi.


Điều gì xảy ra nếu _log.Debug ném ngoại lệ (vì bất kỳ lý do gì)?
JohnL

Sau đó .Debug () ăn ngoại lệ và tôi không bao giờ biết về nó. Nhưng tôi cảm thấy tự tin hơn rất nhiều về logger của mình so với tôi cảm nhận về mã thường ném ngoại lệ. Nếu tôi lo lắng về điều đó, tôi cho rằng tôi có thể thực hiện một số kiểu mẫu theo dõi để vào khối bắt và lưu nó cho lần sau. Trong mọi trường hợp - lấy điểm
Tim Claason

1

Kiểm tra sự tồn tại là một trường hợp sử dụng tốt:

// If an exception happens, it doesn't matter. Log the initial value or the new value

function logId(person) {
    let id = 'No ID';
    try {
        id = person.data.id;
    } catch {}
    console.log(id);
}

Một trường hợp khác là khi một finallymệnh đề được sử dụng:

 function getter(obj){}

 function objChecker()
   {
   try
     {
     /* Check argument length and constructor type */
     if (getter.length === 1 && getter.constructor === Function)
       {
       console.log("Correct implementation");
       return true;
       }
     else
       {
       console.log("Wrong implementation");
       return false;
       }
     }
   catch(e)
     {
     }
   finally
     {
     console.log(JSON.stringify(getter))
     }
   }

 // Test the override of the getter method 
 getter = getter;
 objChecker();
 getter = [];
 objChecker();
 getter = {};
 objChecker();
 getter = RegExp;
 objChecker();
 getter = Function;
 objChecker();

Có một đề xuất cho phép bỏ qua ràng buộc bắt trong ECMAScript liên quan đến trường hợp sử dụng này và các trường hợp sử dụng tương tự.

Người giới thiệu


Một ví dụ khác: Trong nodejs, phương pháp fs.exists (mà trả về true hoặc false) bị phản đối ( nodejs.org/dist/latest-v10.x/docs/api/... ) ủng hộ fs.access đó ném một ngoại lệ trong trường hợp một tập tin không tồn tại. Nếu một người chỉ muốn làm một cái gì đó nếu tập tin tồn tại, mệnh đề bắt là hoàn toàn không cần thiết.
systemovich

0

Lần duy nhất tôi thực sự cần phải làm một cái gì đó như thế này là với một lớp ghi nhật ký cho một ứng dụng giao diện điều khiển. Có một trình xử lý bắt toàn cầu cấp cao nhất đã phát ra lỗi thành STDOUT, ghi lại lỗi vào một tệp, sau đó đóng ứng dụng với mã lỗi> 0.

Vấn đề ở đây là đôi khi, việc đăng nhập vào tập tin thất bại. Quyền truy cập thư mục ngăn bạn viết tệp, tệp bị khóa riêng, bất cứ điều gì. Nếu điều đó xảy ra, tôi không thể xử lý ngoại lệ giống như cách tôi đã có ở nơi khác (bắt, ghi nhật ký các chi tiết có liên quan , bọc trong một loại ngoại lệ tốt hơn, v.v.) vì việc ghi nhật ký các chi tiết ngoại lệ khiến trình ghi chép phải trải qua các hành động tương tự ( cố gắng ghi vào một tập tin) đã thất bại. Khi họ thất bại lần nữa ... Bạn thấy tôi đang đi đâu. Nó được vào một vòng lặp vô hạn.

Nếu bạn bỏ một số khối {} thử, bạn có thể kết thúc ngoại lệ xuất hiện trong hộp thông báo (không phải những gì bạn muốn cho ứng dụng cấu hình im lặng). Vì vậy, trong phương thức ghi vào tệp nhật ký, tôi có một trình xử lý bắt trống. Điều này không chôn vùi lỗi vì bạn vẫn nhận được mã lỗi> 0, nó chỉ dừng vòng lặp vô hạn và cho phép ứng dụng thoát một cách duyên dáng.

Có một bình luận tất nhiên, giải thích tại sao nó trống rỗng.

(Tôi sẽ đăng bài này sớm hơn, tôi chỉ sợ bị vùi vào quên lãng. Mặc dù tôi không phải là người duy nhất, mặc dù ...)


0

Sẽ ổn trong trường hợp khi một số trình tự phải được thực hiện bất kể điều gì với phần quan trọng nhất được đặt ở cuối.

Ví dụ. Trong giao tiếp điều khiển chuyển động của máy chủ để điều khiển. Trong các tình huống khẩn cấp, có một số bước chuẩn bị để hủy bỏ chuyển động, ngừng tạo quỹ đạo, giảm tốc động cơ, cho phép ngắt, vô hiệu hóa bộ khuếch đại và sau đó, bạn phải tiêu diệt năng lượng xe buýt. Tất cả phải diễn ra tuần tự và nếu một trong các bước không thành công thì các bước còn lại phải được thực hiện ngay cả khi có nguy cơ làm hỏng phần cứng được chấp nhận. Nói ngay cả khi tất cả các bên trên không thành công, sức mạnh xe buýt giết chết dù sao cũng phải xảy ra.


Điều đó không có nghĩa là bạn không làm gì cả, chỉ là những gì bạn làm không hủy bỏ dòng chảy. Nắm bắt ngoại lệ của bạn, lưu chúng trong bộ nhớ (không trì hoãn ghi đĩa), sau đó tiêu diệt năng lượng trong một tuyên bố cuối cùng. Sau đó, làm những gì bạn sẽ với các thông tin đã lưu.
Loren Pechtel

0

Đóng tài nguyên hệ thống một cách duyên dáng để phục hồi từ một lỗi

Ví dụ. Nếu bạn đang chạy một dịch vụ trong nền mà không cung cấp thông tin phản hồi cho người dùng, bạn có thể không muốn đẩy một dấu hiệu của lỗi cho người dùng nhưng bạn làm cần phải đóng ra các nguồn lực được sử dụng một cách duyên dáng.

try
{
    // execution code here
}
catch(Exception e)
{
    // do nothing here
}
finally
{
    // close db connection
    // close file io resource
    // close memory stream
    // etc...
}

Lý tưởng nhất là bạn sử dụng lệnh bắt trong chế độ gỡ lỗi để bắt lỗi và in chúng ra bàn điều khiển hoặc vào tệp nhật ký để kiểm tra. Trong môi trường sản xuất, các dịch vụ nền thường được dự kiến ​​sẽ không làm gián đoạn người dùng khi ném lỗi để đầu ra lỗi phải được loại bỏ.

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.