Chi phí thực sự của việc thử / bắt trong C # là gì?


94

Vì vậy, tôi biết rằng thử / bắt không thêm một số chi phí và do đó không phải là cách tốt để kiểm soát luồng quy trình, nhưng chi phí này đến từ đâu và tác động thực tế của nó là gì?

Câu trả lời:


52

Tôi không phải là một chuyên gia về triển khai ngôn ngữ (vì vậy hãy hiểu điều này với một chút muối), nhưng tôi nghĩ rằng một trong những chi phí lớn nhất là giải nén ngăn xếp và lưu trữ nó để theo dõi ngăn xếp. Tôi nghi ngờ điều này chỉ xảy ra khi ngoại lệ được ném (nhưng tôi không biết), và nếu vậy, điều này sẽ được định cỡ chi phí ẩn mỗi khi một ngoại lệ được ném ... vì vậy nó không giống như bạn chỉ nhảy từ một nơi trong mã này sang mã khác, có rất nhiều thứ đang diễn ra.

Tôi không nghĩ đó là vấn đề miễn là bạn đang sử dụng các ngoại lệ cho hành vi NGOẠI LỆ (vì vậy không phải là đường dẫn thông thường, được mong đợi của bạn thông qua chương trình).


33
Chính xác hơn: thử thì rẻ, bắt thì rẻ, ném thì đắt. Nếu bạn tránh thử và bắt, ném vẫn còn đắt.
Lập trình viên Windows

4
Hmmm - đánh dấu không hoạt động trong nhận xét. Để thử lại - ngoại lệ dành cho lỗi, không dành cho "hành vi đặc biệt" hoặc điều kiện: blog.msdn.com/kcwalina/archive/2008/07/17/…
HTTP 410

2
@Windows lập trình viên Số liệu thống kê / nguồn vui lòng?
Kapé

100

Ba điểm cần thực hiện ở đây:

  • Thứ nhất, có rất ít hoặc KHÔNG có hình phạt hiệu suất khi thực sự có các khối thử bắt trong mã của bạn. Điều này không nên được cân nhắc khi cố gắng tránh có chúng trong ứng dụng của bạn. Bản hit hiệu suất chỉ phát huy tác dụng khi một ngoại lệ được ném ra.

  • Khi một ngoại lệ được ném ra ngoài các hoạt động giải nén ngăn xếp, v.v. diễn ra mà những người khác đã đề cập, bạn nên lưu ý rằng một loạt các nội dung liên quan đến thời gian chạy / phản chiếu xảy ra để điền các thành viên của lớp ngoại lệ như dấu vết ngăn xếp đối tượng và các thành viên loại khác nhau, v.v.

  • Tôi tin rằng đây là một trong những lý do tại sao lời khuyên chung nếu bạn định xây lại ngoại lệ là throw;thay vì ném lại ngoại lệ hoặc tạo một ngoại lệ mới vì trong những trường hợp đó, tất cả thông tin ngăn xếp đó được thu thập trong khi đơn giản ném nó là tất cả được bảo tồn.


41
Ngoài ra: Khi bạn ném lại một ngoại lệ là "throw ex" thì bạn sẽ mất dấu vết ngăn xếp ban đầu và thay thế bằng dấu vết ngăn xếp HIỆN TẠI; hiếm khi những gì muốn. Nếu bạn chỉ "ném" thì dấu vết ngăn xếp ban đầu trong Ngoại lệ được giữ nguyên.
Eddie

@Eddie Hoặc throw new Exception("Wrapping layer’s error", ex);
binki

21

Bạn đang hỏi về chi phí của việc sử dụng try / catch / last khi các ngoại lệ không được ném ra, hoặc chi phí của việc sử dụng các ngoại lệ để kiểm soát luồng quy trình? Cách thứ hai tương tự như việc sử dụng một thanh thuốc nổ để thắp sáng ngọn nến sinh nhật của trẻ mới biết đi, và chi phí liên quan rơi vào các khu vực sau:

  • Bạn có thể mong đợi các lần bỏ lỡ bộ nhớ cache bổ sung do ngoại lệ ném truy cập dữ liệu thường trú không bình thường trong bộ nhớ cache.
  • Bạn có thể gặp lỗi trang bổ sung do ngoại lệ được ném ra khi truy cập mã không thường trú và dữ liệu không bình thường trong tập hợp hoạt động của ứng dụng của bạn.

    • ví dụ: ném ngoại lệ sẽ yêu cầu CLR tìm vị trí của các khối cuối cùng và bắt dựa trên IP hiện tại và IP trả về của mọi khung cho đến khi ngoại lệ được xử lý cộng với khối lọc.
    • chi phí xây dựng bổ sung và độ phân giải tên để tạo khung cho mục đích chẩn đoán, bao gồm cả việc đọc siêu dữ liệu, v.v.
    • cả hai mục trên thường truy cập mã và dữ liệu "nguội", do đó, lỗi trang cứng có thể xảy ra nếu bạn có áp lực bộ nhớ:

      • CLR cố gắng đặt mã và dữ liệu được sử dụng không thường xuyên khác xa với dữ liệu được sử dụng thường xuyên để cải thiện tính cục bộ, vì vậy điều này có tác dụng chống lại bạn vì bạn đang buộc cái lạnh trở nên thật nóng.
      • chi phí của lỗi trang cứng, nếu có, sẽ làm giảm mọi thứ khác.
  • Các tình huống bắt điển hình thường sâu, do đó các hiệu ứng trên sẽ có xu hướng được phóng đại (tăng khả năng lỗi trang).

Đối với tác động thực tế của chi phí, điều này có thể thay đổi rất nhiều tùy thuộc vào những gì khác đang diễn ra trong mã của bạn vào thời điểm đó. Jon Skeet có một bản tóm tắt tốt ở đây , với một số liên kết hữu ích. Tôi có xu hướng đồng ý với tuyên bố của anh ấy rằng nếu bạn đi đến điểm mà các ngoại lệ đang ảnh hưởng đáng kể đến hiệu suất của bạn, thì bạn có vấn đề về việc sử dụng các ngoại lệ ngoài hiệu suất.


6

Theo kinh nghiệm của tôi, chi phí lớn nhất là thực sự đưa ra một ngoại lệ và xử lý nó. Tôi đã từng làm việc trong một dự án nơi mã tương tự như sau được sử dụng để kiểm tra xem ai đó có quyền chỉnh sửa đối tượng nào đó hay không. Phương thức HasRight () này được sử dụng ở mọi nơi trong lớp trình bày và thường được gọi cho 100 đối tượng.

bool HasRight(string rightName, DomainObject obj) {
  try {
    CheckRight(rightName, obj);
    return true;
  }
  catch (Exception ex) {
    return false;
  }
}

void CheckRight(string rightName, DomainObject obj) {
  if (!_user.Rights.Contains(rightName))
    throw new Exception();
}

Khi cơ sở dữ liệu thử nghiệm đầy đủ hơn với dữ liệu thử nghiệm, điều này dẫn đến sự chậm lại rất rõ ràng trong khi mở các biểu mẫu mới, v.v.

Vì vậy, tôi đã cấu trúc lại nó như sau, mà - theo các phép đo nhanh hơn sau này - nhanh hơn khoảng 2 bậc độ lớn:

bool HasRight(string rightName, DomainObject obj) {
  return _user.Rights.Contains(rightName);
}

void CheckRight(string rightName, DomainObject obj) {
  if (!HasRight(rightName, obj))
    throw new Exception();
}

Vì vậy, trong ngắn hạn, sử dụng ngoại lệ trong luồng quy trình bình thường chậm hơn khoảng hai bậc độ lớn sau đó sử dụng luồng quy trình tương tự mà không có ngoại lệ.


1
Tại sao bạn muốn ném một ngoại lệ ở đây? Bạn có thể xử lý trường hợp không có quyền ngay tại chỗ.
ThunderGr

@ThunderGr đó thực sự là những gì tôi đã thay đổi, làm cho nó nhanh hơn hai bậc của cường độ.
Tobi

5

Trái ngược với các lý thuyết thường được chấp nhận, try/ catchcó thể có những tác động đáng kể về hiệu suất, và đó là liệu một ngoại lệ có được ném ra hay không!

  1. Nó vô hiệu hóa một số tối ưu hóa tự động (theo thiết kế) , và trong một số trường hợp, đưa mã gỡ lỗi vào , như bạn có thể mong đợi từ một công cụ hỗ trợ gỡ lỗi . Sẽ luôn có những người không đồng ý với tôi về điểm này, nhưng ngôn ngữ đòi hỏi điều đó và sự tháo gỡ cho thấy điều đó nên những người đó theo định nghĩa từ điển là ảo tưởng .
  2. Nó có thể tác động tiêu cực khi bảo trì. Đây thực sự là vấn đề quan trọng nhất ở đây, nhưng vì câu trả lời cuối cùng của tôi (tập trung gần như hoàn toàn vào nó) đã bị xóa, tôi sẽ cố gắng tập trung vào vấn đề ít quan trọng hơn (tối ưu hóa vi mô) thay vì vấn đề quan trọng hơn ( tối ưu hóa vĩ mô).

Trước đây đã được bao phủ trong một vài bài đăng trên blog của Microsoft MVP trong những năm qua, và tôi tin tưởng bạn có thể tìm thấy chúng một cách dễ dàng chưa StackOverflow quan tâm rất nhiều về nội dung vì vậy tôi sẽ cung cấp các liên kết đến một số trong số họ là phụ bằng chứng:

  • Hàm ý về hiệu suất của try/ catch/finally ( và phần hai ), của Peter Ritchie khám phá các tính năng tối ưu màtry/catch/finallyvô hiệu hóa (và tôi sẽ đi sâu hơn vào vấn đề này với các trích dẫn từ tiêu chuẩn)
  • Hồ sơ hiệu suất Parseso với so TryParsevớiConvertTo của Ian Huff tuyên bố trắng trợn rằng "việc xử lý ngoại lệ rất chậm" và thể hiện điểm này bằng cách phân loạiInt.ParseInt.TryParsechống lại nhau ... Đối với bất kỳ ai khăng khăng rằngTryParsesử dụngtry/catchhậu trường, điều này nên làm sáng tỏ!

Cũng có câu trả lời này cho thấy sự khác biệt giữa mã được tháo rời có- và không sử dụng try/ catch.

Có vẻ như quá rõ ràng rằng có một chi phí mà là trắng trợn thể quan sát được trong việc tạo mã, và chi phí mà ngay cả dường như được công nhận bởi những người Microsoft giá trị! Tuy nhiên, tôi đang lặp lại internet ...

Có, có hàng tá hướng dẫn MSIL bổ sung cho một dòng mã tầm thường và điều đó thậm chí không bao gồm các tối ưu bị vô hiệu hóa nên về mặt kỹ thuật, đó là một tối ưu hóa vi mô.


Tôi đã đăng một câu trả lời cách đây nhiều năm đã bị xóa vì nó tập trung vào năng suất của các lập trình viên (tối ưu hóa vĩ mô).

Điều này thật đáng tiếc vì không tiết kiệm được vài nano giây ở đây và ở đó thời gian CPU có thể bù đắp cho nhiều giờ tối ưu hóa thủ công tích lũy của con người. Sếp của bạn trả nhiều tiền hơn cho cái nào: một giờ dành cho bạn hay một giờ với máy tính đang chạy? Tại thời điểm nào chúng ta rút phích cắm và thừa nhận rằng đã đến lúc mua một chiếc máy tính nhanh hơn ?

Rõ ràng, chúng ta nên tối ưu hóa các ưu tiên của mình , không chỉ mã của chúng ta! Trong câu trả lời cuối cùng của tôi, tôi đã rút ra sự khác biệt giữa hai đoạn mã.

Sử dụng try/ catch:

int x;
try {
    x = int.Parse("1234");
}
catch {
    return;
}
// some more code here...

Không sử dụng try/ catch:

int x;
if (int.TryParse("1234", out x) == false) {
    return;
}
// some more code here

Hãy xem xét từ quan điểm của một nhà phát triển bảo trì, điều này có nhiều khả năng làm lãng phí thời gian của bạn hơn, nếu không phải trong việc lập hồ sơ / tối ưu hóa (đã đề cập ở trên) mà thậm chí sẽ không cần thiết nếu nó không phải là vấn đề try/ catch, sau đó cuộn qua mã nguồn ... Một trong số đó có thêm bốn dòng rác soạn sẵn!

Khi ngày càng có nhiều trường được đưa vào một lớp, tất cả rác của bản soạn sẵn này sẽ tích tụ (cả trong mã nguồn và mã được tháo rời) vượt quá mức hợp lý. Bốn dòng thừa trên mỗi trường, và chúng luôn luôn giống nhau ... Chúng ta không được dạy để tránh lặp lại chính mình sao? Tôi cho rằng chúng ta có thể ẩn try/ catchđằng sau một số trừu tượng tự pha chế, nhưng ... sau đó chúng ta cũng có thể tránh các ngoại lệ (tức là sử dụngInt.TryParse ).

Đây thậm chí không phải là một ví dụ phức tạp; Tôi đã thấy những nỗ lực khởi tạo các lớp mới trong try/ catch. Hãy xem xét rằng tất cả mã bên trong của hàm tạo sau đó có thể bị loại khỏi một số tối ưu nhất định mà nếu không trình biên dịch sẽ tự động áp dụng. Còn cách nào tốt hơn để đưa ra giả thuyết rằng trình biên dịch chậm , trái ngược với việc trình biên dịch đang làm chính xác những gì nó được yêu cầu ?

Giả sử một ngoại lệ được tạo bởi nhà xây dựng nói trên và một số lỗi được kích hoạt do đó, nhà phát triển bảo trì kém sau đó phải theo dõi nó. Đó có thể không phải là một nhiệm vụ dễ dàng như vậy, vì không giống như mã spaghetti của cơn ác mộng goto , try/ catchcó thể gây ra sự lộn xộn trong ba chiều , vì nó có thể di chuyển ngăn xếp thành không chỉ các phần khác của cùng một phương thức mà còn cả các lớp và phương thức khác , tất cả sẽ được quan sát bởi nhà phát triển bảo trì, một cách khó khăn ! Tuy nhiên, chúng tôi được nói rằng "goto là nguy hiểm", heh!

Ở phần cuối, tôi đề cập, try/ catchcó lợi ích của nó, đó là, nó được thiết kế để vô hiệu hóa tính năng tối ưu hóa ! Nếu bạn muốn, nó là một trợ giúp gỡ lỗi ! Đó là những gì nó được thiết kế cho và đó là những gì nó nên được sử dụng như ...

Tôi đoán đó cũng là một điểm tích cực. Nó có thể được sử dụng để vô hiệu hóa các tối ưu hóa có thể làm tê liệt các thuật toán truyền thông điệp an toàn, lành mạnh cho các ứng dụng đa luồng và để nắm bắt các điều kiện cuộc đua có thể xảy ra;) Đó là về kịch bản duy nhất tôi có thể nghĩ đến để sử dụng try / catch. Thậm chí điều đó có các lựa chọn thay thế.


Có gì optimisations làm try, catchfinallyvô hiệu hóa?

AKA

Làm thế nào là try, catchfinallyhữu ích như gỡ lỗi trợ?

chúng là rào cản viết. Điều này xuất phát từ tiêu chuẩn:

12.3.3.13 Câu lệnh try-catch

Đối với một câu lệnh stmt có dạng:

try try-block
catch ( ... ) catch-block-1
... 
catch ( ... ) catch-block-n
  • Trạng thái gán xác định của v ở đầu khối try cũng giống như trạng thái gán xác định của v ở đầu stmt .
  • Trạng thái gán xác định của v ở đầu catch-block-i (với i bất kỳ ) cũng giống như trạng thái gán xác định của v ở đầu stmt .
  • Trạng thái gán xác định của v tại điểm cuối của stmt chắc chắn được gán nếu (và chỉ khi) v chắc chắn được gán tại điểm cuối của khối try và mọi khối bắt-i (với mọi i từ 1 đến n ).

Nói cách khác, ở đầu mỗi trycâu lệnh:

  • tất cả các nhiệm vụ được thực hiện cho các đối tượng hiển thị trước khi nhập trycâu lệnh phải hoàn thành, điều này yêu cầu khóa luồng để bắt đầu, điều này rất hữu ích cho việc gỡ lỗi các điều kiện cuộc đua!
  • trình biên dịch không được phép:
    • loại bỏ các phép gán biến không sử dụng mà chắc chắn đã được gán trước trycâu lệnh
    • tổ chức lại hoặc liên kết bất kỳ nhiệm vụ nội bộ nào của nó (tức là xem liên kết đầu tiên của tôi, nếu bạn chưa làm như vậy).
    • nâng các nhiệm vụ qua rào cản này, để trì hoãn việc gán cho một biến mà nó biết rằng nó sẽ không được sử dụng cho đến sau này (nếu có) hoặc để chuyển trước các nhiệm vụ sau về phía trước để có thể thực hiện các tối ưu hóa khác ...

Một câu chuyện tương tự xảy ra cho mỗi catchtuyên bố; giả sử trong trycâu lệnh của bạn (hoặc một phương thức khởi tạo hoặc hàm mà nó gọi ra, v.v.) bạn gán cho biến vô nghĩa đó (giả sử, garbage=42;), trình biên dịch không thể loại bỏ câu lệnh đó, bất kể nó không liên quan đến hành vi quan sát được của chương trình. . Nhiệm vụ cần phải hoàn thành trước khi catchkhối được nhập.

Đối với những gì nó đáng giá, hãy finallykể một câu chuyện suy thoái tương tự :

12.3.3.14 Câu lệnh thử cuối cùng

Đối với một câu lệnh thử stmt có dạng:

try try-block
finally finally-block

• Trạng thái gán xác định của v ở đầu khối try cũng giống như trạng thái gán xác định của v ở đầu stmt .
• Trạng thái gán xác định của v ở đầu khối cuối cùng cũng giống như trạng thái gán xác định của v ở đầu stmt .
• Trạng thái gán xác định của v tại điểm cuối của stmt chắc chắn được gán nếu (và chỉ khi) một trong hai: o v chắc chắn được gán ở điểm cuối của khối thử o vchắc chắn được chỉ định ở điểm cuối của khối cuối cùng Nếu việc chuyển luồng điều khiển (chẳng hạn như câu lệnh goto ) được thực hiện bắt đầu bên trong khối try và kết thúc bên ngoài khối thử , thì v cũng được coi là chắc chắn được chỉ định trên đó điều khiển chuyển luồng nếu v chắc chắn được gán tại điểm cuối của khối cuối cùng . (Đây không phải là chỉ nếu — nếu v chắc chắn được chỉ định vì một lý do khác trong quá trình chuyển luồng điều khiển này, thì nó vẫn được coi là chắc chắn được gán.)

12.3.3.15 Câu lệnh try-catch-last

Phân tích nhiệm vụ xác định cho một câu lệnh try - catch - last của biểu mẫu:

try try-block
catch ( ... ) catch-block-1
... 
catch ( ... ) catch-block-n
finally finally-block

được thực hiện như thể tuyên bố là một thử - cuối cùng đã tuyên bố kèm theo một thử - bắt tuyên bố:

try { 
    try   
    try-block
    catch ( ... ) catch-block-1
    ...   
    catch ( ... ) catch-block-n
} 
finally finally-block

3

Chưa kể nếu nó nằm trong một phương thức thường được gọi, nó có thể ảnh hưởng đến hành vi tổng thể của ứng dụng.
Ví dụ, tôi coi việc sử dụng Int32.Parse là một phương pháp không tốt trong hầu hết các trường hợp vì nó ném ra các ngoại lệ cho một số thứ có thể dễ dàng bị bắt.

Vì vậy, để kết thúc mọi thứ được viết ở đây:
1) Sử dụng khối try..catch để bắt lỗi không mong muốn - hầu như không có hình phạt hiệu suất.
2) Không sử dụng ngoại lệ cho các lỗi ngoại lệ nếu bạn có thể tránh được.


3

Tôi đã viết một bài báo về điều này một thời gian trước vì có rất nhiều người hỏi về điều này vào thời điểm đó. Bạn có thể tìm thấy nó và mã kiểm tra tại http://www.blackwasp.co.uk/SpeedTestTryCatch.aspx .

Kết quả là có một lượng nhỏ chi phí cho một khối thử / bắt nhưng quá nhỏ nên nó nên được bỏ qua. Tuy nhiên, nếu bạn đang chạy khối try / catch trong các vòng lặp được thực thi hàng triệu lần, bạn có thể cân nhắc việc di chuyển khối ra bên ngoài vòng lặp nếu có thể.

Vấn đề hiệu suất quan trọng với các khối try / catch là khi bạn thực sự bắt được một ngoại lệ. Điều này có thể gây ra sự chậm trễ đáng kể cho ứng dụng của bạn. Tất nhiên, khi mọi thứ diễn ra không như ý muốn, hầu hết các nhà phát triển (và rất nhiều người dùng) nhận ra việc tạm dừng là một ngoại lệ sắp xảy ra! Chìa khóa ở đây là không sử dụng xử lý ngoại lệ cho các hoạt động bình thường. Như tên cho thấy, chúng rất đặc biệt và bạn nên làm mọi thứ có thể để tránh chúng bị ném. Bạn không nên sử dụng chúng như một phần của luồng dự kiến ​​của một chương trình đang hoạt động chính xác.


2

Tôi đã thực hiện một mục blog về chủ đề này vào năm ngoái. Kiểm tra nó ra. Điểm mấu chốt là hầu như không có chi phí cho khối thử nếu không có ngoại lệ nào xảy ra - và trên máy tính xách tay của tôi, một ngoại lệ là khoảng 36μs. Điều đó có thể ít hơn bạn mong đợi, nhưng hãy nhớ rằng những kết quả đó ở mức thấp. Ngoài ra, các ngoại lệ đầu tiên thực sự chậm.


Tôi không thể truy cập blog của bạn (kết nối hết thời gian; bạn có đang sử dụng try/ catchquá nhiều không? Heh heh), nhưng có vẻ như bạn đang tranh cãi với thông số ngôn ngữ và một số MS MVP cũng đã viết blog về chủ đề này, cung cấp các phép đo trái với lời khuyên của bạn ... Tôi cởi mở với gợi ý rằng nghiên cứu tôi đã thực hiện là sai, nhưng tôi cần đọc mục blog của bạn để xem nó nói gì.
tự kỷ

Ngoài bài đăng trên blog của @ Hafthor, đây là một bài đăng trên blog khác có mã được viết riêng để kiểm tra sự khác biệt về hiệu suất tốc độ. Theo kết quả, nếu bạn có một ngoại lệ xảy ra thậm chí chỉ 5% thời gian, thì mã Xử lý Ngoại lệ nói chung chạy chậm hơn 100 lần so với mã xử lý không ngoại lệ. Bài viết nhắm mục tiêu cụ thể đến try-catchkhối so với tryparse()các phương thức, nhưng khái niệm thì giống nhau.

2

Việc viết, gỡ lỗi và duy trì mã dễ dàng hơn nhiều mà không có thông báo lỗi trình biên dịch, thông báo cảnh báo phân tích mã và các ngoại lệ được chấp nhận thông thường (đặc biệt là các ngoại lệ được ném ở một nơi và được chấp nhận ở nơi khác). Bởi vì nó dễ dàng hơn, mã trung bình sẽ được viết tốt hơn và ít lỗi hơn.

Đối với tôi, lập trình viên và chi phí chất lượng là lý lẽ chính để chống lại việc sử dụng try-catch cho quy trình.

Chi phí máy tính của các trường hợp ngoại lệ là không đáng kể khi so sánh và thường là rất nhỏ về khả năng của ứng dụng để đáp ứng các yêu cầu hiệu suất trong thế giới thực.


@Ritchard T, tại sao? So với chi phí lập trình viên và chất lượng thì nó không đáng kể.
ThunderGr

1

Tôi thực sự thích bài đăng trên blog của Hafthor , và để thêm hai xu của tôi vào cuộc thảo luận này, tôi muốn nói rằng, tôi luôn dễ dàng để DỮ LIỆU chỉ ném một loại ngoại lệ (DataAccessException). Bằng cách này, NHÀ KINH DOANH của tôi biết ngoại lệ nào sẽ xảy ra và nắm bắt nó. Sau đó, tùy thuộc vào các quy tắc nghiệp vụ khác (ví dụ: nếu đối tượng kinh doanh của tôi tham gia vào quy trình làm việc, v.v.), tôi có thể đưa ra một ngoại lệ mới (BusinessObjectException) hoặc tiếp tục mà không cần / ném lại.

Tôi muốn nói rằng đừng ngần ngại sử dụng try..catch bất cứ khi nào cần thiết và sử dụng nó một cách khôn ngoan!

Ví dụ: phương pháp này tham gia vào một quy trình làm việc ...

Bình luận?

public bool DeleteGallery(int id)
{
    try
    {
        using (var transaction = new DbTransactionManager())
        {
            try
            {
                transaction.BeginTransaction();

                _galleryRepository.DeleteGallery(id, transaction);
                _galleryRepository.DeletePictures(id, transaction);

                FileManager.DeleteAll(id);

                transaction.Commit();
            }
            catch (DataAccessException ex)
            {
                Logger.Log(ex);
                transaction.Rollback();                        
                throw new BusinessObjectException("Cannot delete gallery. Ensure business rules and try again.", ex);
            }
        }
    }
    catch (DbTransactionException ex)
    {
        Logger.Log(ex);
        throw new BusinessObjectException("Cannot delete gallery.", ex);
    }
    return true;
}

2
David, bạn có muốn gọi 'DeleteGallery' trong một khối thử / bắt không?
Cướp

Vì DeleteGallery là một hàm Boolean, đối với tôi, có vẻ như việc ném một ngoại lệ vào đó là không hữu ích. Điều này sẽ yêu cầu lệnh gọi DeleteGallery được bao bọc trong một khối try / catch. Đối với tôi, một if (! DeleteGallery (theid)) {// handle} có vẻ có ý nghĩa hơn. trong ví dụ cụ thể đó.
ThunderGr

1

Chúng ta có thể đọc trong Ngôn ngữ lập trình Ngữ dụng của Michael L. Scott rằng các trình biên dịch ngày nay không thêm bất kỳ chi phí nào trong trường hợp phổ biến, điều này có nghĩa là, khi không có ngoại lệ nào xảy ra. Vì vậy, mọi tác phẩm được thực hiện trong thời gian biên dịch. Nhưng khi một ngoại lệ được đưa ra trong thời gian chạy, trình biên dịch cần thực hiện tìm kiếm nhị phân để tìm ngoại lệ chính xác và điều này sẽ xảy ra cho mọi lần ném mới mà bạn đã thực hiện.

Nhưng các trường hợp ngoại lệ là ngoại lệ và chi phí này hoàn toàn có thể chấp nhận được. Nếu bạn cố gắng thực hiện Xử lý ngoại lệ mà không có ngoại lệ và thay vào đó sử dụng mã lỗi trả về, có thể bạn sẽ cần câu lệnh if cho mọi chương trình con và điều này sẽ phát sinh chi phí thực sự theo thời gian thực. Bạn biết câu lệnh if được chuyển đổi thành một vài lệnh hợp ngữ, lệnh này sẽ được thực hiện mỗi khi bạn nhập vào các quy trình con của mình.

Xin lỗi về tiếng Anh của tôi, hy vọng rằng nó sẽ giúp bạn. Thông tin này dựa trên sách đã trích dẫn, để biết thêm thông tin, hãy tham khảo Chương 8.5 Xử lý ngoại lệ.


Trình biên dịch không hoạt động trong thời gian chạy. Phải một chi phí cho các khối thử / bắt để CLR có thể xử lý các ngoại lệ. C # chạy trên .NET CLR (một máy ảo). Đối với tôi, dường như tổng chi phí của khối là tối thiểu khi không có ngoại lệ nhưng chi phí của CLR xử lý ngoại lệ là rất đáng kể.
ThunderGr

-4

Hãy để chúng tôi phân tích một trong những chi phí lớn nhất có thể có của khối thử / bắt khi được sử dụng ở những nơi không cần sử dụng:

int x;
try {
    x = int.Parse("1234");
}
catch {
    return;
}
// some more code here...

Và đây là cái mà không cần thử / bắt:

int x;
if (int.TryParse("1234", out x) == false) {
    return;
}
// some more code here

Không tính khoảng trắng không đáng kể, người ta có thể nhận thấy rằng hai đoạn mã tương đương này gần như chính xác cùng độ dài tính bằng byte. Cái sau chứa ít hơn 4 byte thụt lề. Đó có phải là một điều xấu?

Để thêm sự xúc phạm đến thương tích, một sinh viên quyết định lặp lại trong khi đầu vào có thể được phân tích cú pháp dưới dạng int. Giải pháp mà không cần try / catch có thể là:

while (int.TryParse(...))
{
    ...
}

Nhưng điều này trông như thế nào khi sử dụng try / catch?

try {
    for (;;)
    {
        x = int.Parse(...);
        ...
    }
}
catch
{
    ...
}

Thử / bắt khối là cách kỳ diệu để lãng phí thụt lề và chúng tôi thậm chí vẫn không biết lý do nó không thành công! Hãy tưởng tượng người thực hiện gỡ lỗi cảm thấy như thế nào, khi mã tiếp tục thực thi qua một lỗ hổng logic nghiêm trọng, thay vì dừng lại với một lỗi ngoại lệ rõ ràng. Các khối try / catch là xác thực / vệ sinh dữ liệu của một kẻ lười biếng.

Một trong những chi phí nhỏ hơn là khối try / catch thực sự vô hiệu hóa một số tối ưu hóa nhất định: http://msmvps.com/blogs/peterritchie/archive/2007/06/22/performance-implication-of-try-catch-finally.aspx . Tôi đoán đó cũng là một điểm tích cực. Nó có thể được sử dụng để vô hiệu hóa các tối ưu hóa có thể làm tê liệt các thuật toán truyền thông báo an toàn, lành mạnh cho các ứng dụng đa luồng và để nắm bắt các điều kiện cuộc đua có thể xảy ra;) Đó là về kịch bản duy nhất tôi có thể nghĩ đến để sử dụng try / catch. Thậm chí điều đó có các lựa chọn thay thế.


1
Tôi khá chắc chắn rằng TryParse thực hiện thử {int x = int.Parse ("xxx"); return true;} catch {return false; } trong nội bộ. Thụt lề không phải là mối quan tâm trong câu hỏi, chỉ là hiệu suất và chi phí.
ThunderGr

@ThunderGr Ngoài ra, hãy đọc câu trả lời mới mà tôi đã đăng. Nó chứa nhiều liên kết hơn, một trong số đó là phân tích về mức tăng hiệu suất lớn khi bạn tránh có Int.Parselợi Int.TryParse.
tự kỷ
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.