it-swarm-vi.com

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

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.

61
ysolik

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 tại sao bạn đang 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 cũ catch {} hầu như luôn luôn là một điều xấu.

78
dsimcha

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ố.

50
kdubinets

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 nhận xét bất cứ điều gì trông giống như một lỗi (dự phòng trong câu lệnh switch, trống catch, gán 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.

8
David Thornley

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 bạn phải xử lý nó? Nhưng bạn không thể đơn giản sử dụng mệnh đề try-Catch.

6
user281377

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;
6
Fil

Các khối catch 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 thực sự không để lại bất kỳ phòng nào cho các khối không làm gì, trống catch.

EDITED TO ADD: giả sử bạn đang lập trình bằng 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ế cho goto). Sau đó, một khối catch 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 khối else trống trong một ngôn ngữ truyền thống (hoặc không có khối else). 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++.

6
azheglov

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.

3
user281377

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 thứ chung 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, thì ít nhất tôi làm là đặt một xác nhận thuộc loại nào đó, vì vậy ít nhất có điều gì đó xảy ra trong 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
DevSolo

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
}
2
Victor Hurdugaci

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.

2
o. nate

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ệnh đề finally đượ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ự.

Tài liệu tham khảo

1
Paul Sweatte

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 triển khai 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, trong quá trình thực hiện trình ghi nhật ký của tôi, 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.

1
Tim Claason

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 Host đến bộ đ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. Nói ngay cả khi tất cả các bên trên không thành công, sức mạnh giết chết xe buýt dù sao cũng phải xảy ra.

0
user7071

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 bảng đ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ý chi tiết có liên quan, bọc trong loại ngoại lệ tốt hơn, v.v.) vì ghi nhật ký chi tiết ngoại lệ gây ra trình ghi nhật ký trải qua các hành động tương tự (cố gắng ghi vào một tệp) đã 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ị cuốn 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
JohnL

Đó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ụ ở chế độ nền không cung cấp phản hồi cho người dùng, bạn có thể không muốn đưa ra dấu hiệu lỗi cho người dùng nhưng bạn do cần phải đóng tài nguyên đượ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ỏ.

0
Evan Plaice