Tôi gần như tích cực rằng câu trả lời là CÓ. Nếu tôi sử dụng khối Thử cuối cùng nhưng không sử dụng khối Bắt thì mọi trường hợp ngoại lệ SILL bong bóng. Chính xác?
Bất kỳ suy nghĩ về thực hành nói chung?
Seth
Tôi gần như tích cực rằng câu trả lời là CÓ. Nếu tôi sử dụng khối Thử cuối cùng nhưng không sử dụng khối Bắt thì mọi trường hợp ngoại lệ SILL bong bóng. Chính xác?
Bất kỳ suy nghĩ về thực hành nói chung?
Seth
Câu trả lời:
Vâng, nó hoàn toàn sẽ. Tất nhiên, giả sử finally
khối của bạn không ném ngoại lệ, trong trường hợp đó sẽ "thay thế" một cách hiệu quả cái ban đầu được ném.
Bất kỳ suy nghĩ về thực hành nói chung?
Đúng. Hãy cẩn thận . Khi khối cuối cùng của bạn đang chạy, hoàn toàn có thể nó đang chạy vì một ngoại lệ không mong muốn, chưa được xử lý đã bị ném . Điều đó có nghĩa là một cái gì đó bị phá vỡ , và một cái gì đó hoàn toàn bất ngờ có thể xảy ra.
Trong tình huống đó, có thể nói rằng bạn không nên chạy mã trong các khối cuối cùng. Mã trong khối cuối cùng có thể được xây dựng để giả định rằng các hệ thống con mà nó phụ thuộc vào là lành mạnh, trong khi thực tế chúng có thể bị phá vỡ sâu sắc. Mã trong khối cuối cùng có thể làm cho mọi thứ tồi tệ hơn.
Ví dụ, tôi thường thấy loại điều này:
DisableAccessToTheResource();
try
{
DoSomethingToTheResource();
}
finally
{
EnableAccessToTheResource();
}
Tác giả của mã này đang nghĩ rằng "Tôi đang tạo ra một sự đột biến tạm thời đối với tình trạng của thế giới; tôi cần khôi phục lại trạng thái như trước khi tôi được gọi". Nhưng hãy nghĩ về tất cả những cách điều này có thể đi sai.
Đầu tiên, quyền truy cập vào tài nguyên có thể đã bị vô hiệu hóa bởi người gọi; trong trường hợp đó, mã này kích hoạt lại nó, có thể là sớm.
Thứ hai, nếu DoS SomethingToTheResource ném ra một ngoại lệ, thì có phải làm gì để cho phép truy cập vào tài nguyên không ??? Mã quản lý tài nguyên bị phá vỡ bất ngờ . Mã này cho biết, có hiệu lực "nếu mã quản lý bị hỏng, hãy đảm bảo rằng mã khác có thể gọi mã bị hỏng đó càng sớm càng tốt, để nó cũng có thể thất bại khủng khiếp ." Đây dường như là một ý tưởng tồi.
Thứ ba, nếu DoS SomethingToTheResource ném ra một ngoại lệ, thì làm sao chúng ta biết rằng EnableAccessToTheResource cũng sẽ không ném ngoại lệ? Dù có tệ hại gì đi nữa thì việc sử dụng tài nguyên cũng có thể ảnh hưởng đến mã dọn dẹp, trong trường hợp đó, ngoại lệ ban đầu sẽ bị mất và vấn đề sẽ khó chẩn đoán hơn.
Tôi có xu hướng viết mã như thế này mà không sử dụng các khối thử cuối cùng:
bool wasDisabled = IsAccessDisabled();
if (!wasDisabled)
DisableAccessToTheResource();
DoSomethingToTheResource();
if (!wasDisabled)
EnableAccessToTheResource();
Bây giờ nhà nước không bị đột biến trừ khi nó cần. Bây giờ trạng thái của người gọi không bị rối tung. Và bây giờ, nếu DoS SomethingToTheResource không thành công, thì chúng tôi không kích hoạt lại quyền truy cập. Chúng tôi giả định rằng một cái gì đó bị phá vỡ sâu sắc và không có nguy cơ làm cho tình hình tồi tệ hơn bằng cách cố gắng tiếp tục chạy mã. Hãy để người gọi giải quyết vấn đề, nếu họ có thể.
Vì vậy, khi nào là một ý tưởng tốt để chạy một khối cuối cùng? Đầu tiên, khi ngoại lệ được mong đợi. Ví dụ: bạn có thể mong đợi rằng một nỗ lực để khóa một tệp có thể thất bại, bởi vì người khác đã khóa nó. Trong trường hợp đó, có ý nghĩa để bắt ngoại lệ và báo cáo cho người dùng. Trong trường hợp đó, sự không chắc chắn về những gì bị hỏng được giảm đi; bạn không có khả năng làm cho mọi thứ tồi tệ hơn bằng cách dọn dẹp.
Thứ hai, khi tài nguyên bạn đang dọn dẹp là tài nguyên hệ thống khan hiếm. Ví dụ, sẽ rất hợp lý khi đóng một tệp xử lý trong một khối cuối cùng. ("Sử dụng" tất nhiên chỉ là một cách khác để viết khối thử cuối cùng.) Nội dung của tệp có thể bị hỏng, nhưng hiện tại bạn không thể làm gì về điều đó. Xử lý tập tin cuối cùng sẽ được đóng lại, vì vậy nó cũng có thể sớm hơn thay vì muộn hơn.