Câu hỏi Tại sao ngoại lệ xử lý xấu?


Ngôn ngữ Go của Google không có ngoại lệ như là một lựa chọn thiết kế, và Linus của Linux nổi tiếng đã được gọi là ngoại lệ crap. Tại sao?


76
2017-11-15 00:38


gốc


Tác giả của ZeroMQ viết về cách anh ta nghĩ rằng nó là một sai lầm để viết nó trong C + + (chủ yếu là do xử lý lỗi) 250bpm.com/blog:4 - serbaut
Đi có thể không có ngoại lệ, nhưng nó có "hoảng loạn" mà bạn có thể "phục hồi" từ (trong khi câu lệnh trì hoãn vẫn được thực hiện) và cung cấp luồng kiểm soát không cục bộ ... - Kerrek SB
Đây là một bài viết hay lighterra.com/papers/exceptionsharmful (Xử lý ngoại lệ được coi là có hại) - masterxilo
Afaics, các ngoại lệ có thể được mô phỏng trong Go với bản mẫu đáng kể, mặc dù điểm này có thể có ý nghĩa hơn đối với transpiling từ một đường cú pháp hơn để viết các boilerplate bằng tay. - Shelby Moore III


Các câu trả lời:


Trường hợp ngoại lệ làm cho nó thực sự dễ dàng để viết mã, nơi một ngoại lệ được ném sẽ phá vỡ bất biến và để lại các đối tượng trong một nhà nước không phù hợp. Về cơ bản, họ buộc bạn phải nhớ rằng hầu hết mọi tuyên bố bạn thực hiện đều có khả năng ném và xử lý chính xác. Làm như vậy có thể phức tạp và phản trực giác.

Hãy xem xét một cái gì đó như thế này như là một ví dụ đơn giản:

class Frobber
{
    int m_NumberOfFrobs;
    FrobManager m_FrobManager;

public:
    void Frob()
    {
        m_NumberOfFrobs++;

        m_FrobManager.HandleFrob(new FrobObject());
    }
};

Giả sử FrobManager sẽ delete các FrobObject, điều này có vẻ ổn, phải không? Hoặc có thể không ... Hãy tưởng tượng sau đó nếu FrobManager::HandleFrob() hoặc là operator new ném một ngoại lệ. Trong ví dụ này, sự gia tăng của m_NumberOfFrobs không được khôi phục. Do đó, bất kỳ ai sử dụng trường hợp này Frobber sẽ có một đối tượng có thể bị hỏng.

Ví dụ này có vẻ ngu ngốc (ok, tôi đã phải căng thẳng một chút để xây dựng một :-)), nhưng, takeaway là nếu một lập trình viên không liên tục suy nghĩ về ngoại lệ, và đảm bảo rằng mọi hoán vị của nhà nước được cuộn trở lại bất cứ khi nào có ném, bạn gặp rắc rối theo cách này.

Ví dụ, bạn có thể nghĩ về nó giống như bạn nghĩ về mutexes. Bên trong một phần quan trọng, bạn dựa vào một số câu lệnh để đảm bảo rằng cấu trúc dữ liệu không bị hỏng và các luồng khác không thể thấy các giá trị trung gian của bạn. Nếu bất kỳ một trong những tuyên bố đó chỉ ngẫu nhiên không chạy, bạn sẽ kết thúc trong một thế giới đau đớn. Bây giờ lấy đi ổ khóa và đồng thời, và suy nghĩ về mỗi phương pháp như thế. Hãy suy nghĩ của từng phương pháp như một giao dịch hoán vị về trạng thái đối tượng, nếu bạn muốn. Khi bắt đầu cuộc gọi phương thức của bạn, đối tượng phải là trạng thái sạch và cuối cùng cũng phải có trạng thái sạch. Ở giữa, biến foo có thể không phù hợp với bar, nhưng mã của bạn cuối cùng sẽ khắc phục điều đó. Những ngoại lệ có nghĩa là bất kỳ một trong những phát biểu của bạn có thể làm gián đoạn bạn bất cứ lúc nào. Các onus là ngày bạn trong từng phương pháp cá nhân để làm cho nó đúng và quay trở lại khi điều đó xảy ra, hoặc đặt hàng hoạt động của bạn để ném không ảnh hưởng đến trạng thái đối tượng. Nếu bạn hiểu sai (và thật dễ dàng để mắc sai lầm này), thì người gọi sẽ thấy các giá trị trung gian của bạn.

Các phương thức như RAII, mà các lập trình viên C ++ thích đề cập đến như là giải pháp tối thượng cho vấn đề này, đi một chặng đường dài để bảo vệ chống lại điều này. Nhưng chúng không phải là viên đạn bạc. Nó sẽ đảm bảo bạn phát hành tài nguyên trên một cú ném, nhưng không giải phóng bạn khỏi phải suy nghĩ về tham nhũng của trạng thái đối tượng và người gọi thấy giá trị trung gian. Vì vậy, đối với nhiều người, sẽ dễ dàng hơn khi nói theo phong cách mã hóa, không có ngoại lệ. Nếu bạn hạn chế loại mã bạn viết, khó có thể giới thiệu các lỗi này. Nếu bạn không, nó khá dễ dàng để làm cho một sai lầm.

Toàn bộ sách đã được viết về mã hóa ngoại lệ an toàn trong C ++. Rất nhiều chuyên gia đã hiểu sai. Nếu nó thực sự phức tạp và có nhiều sắc thái, có thể đó là một dấu hiệu tốt mà bạn cần phải bỏ qua tính năng đó. :-)


64
2017-11-15 01:59



Câu trả lời thú vị, tuy nhiên nó không phản ánh bất cứ điều gì trong kinh nghiệm lập trình của tôi. Vì vậy, tôi đoán nó hoặc là văn hóa cụ thể (có thể nhiều hơn một vấn đề trong Java hoặc C + + hơn, nói, Python) hoặc miền cụ thể. - ddaa
Các trường hợp ngoại lệ được viết bằng ngôn ngữ được quản lý bằng cách sử dụng mẫu thử cuối cùng sẽ không bao giờ rời khỏi trạng thái không hợp lệ nếu chúng được viết đúng cách; kể từ khi khối cuối cùng được đảm bảo được thực hiện, các đối tượng có thể được deallocated ở đó. Phần còn lại nên được xử lý bởi các biến đi ngoài phạm vi và thu gom rác thải. - Robert Harvey♦
@ddaa Vấn đề chắc chắn có thể xảy ra trong Python. Kết quả thường khó tái tạo lỗi. Có lẽ bạn đã đặc biệt tỉ mỉ, hoặc may mắn. Nhưng sau đó, bạn nói đúng rằng đó là một vấn đề trong C ++, nơi mà lỗi phổ biến nhất từ ​​EH kém là rò rỉ bộ nhớ. Tôi cố gắng nhấn mạnh rằng rò rỉ không phải là vấn đề nghiêm trọng nhất. @Robert GC sẽ giảm thiểu rò rỉ bộ nhớ, nhưng tôi không chắc chắn mã được quản lý sẽ giải phóng bạn khỏi lỗi lập trình viên. Đặc biệt nếu ai đó không chú ý đến sự an toàn ngoại lệ bởi vì họ không nghĩ rằng đó là một vấn đề trong ngôn ngữ của họ, đó không phải là một dấu hiệu tuyệt vời. - asveikau
@ lzprgmr có chắc chắn là: trường hợp ngoại lệ cho phép bạn đối phó với diff. các loại lỗi khác nhau. vị trí trong mã. Xử lý lỗi kết nối có thể yêu cầu kết nối lại, nhưng không phải ở giữa hàm lồng nhau sâu. Bạn muốn bong bóng lên đến người quản lý kết nối hoặc một cái gì đó. Sau đó, đối phó với các giá trị trả về buộc bạn phải kiểm tra lỗi trên từng cuộc gọi và bong bóng lên theo cách thủ công (trong trường hợp xảy ra lỗi kết nối lại cho ví dụ cũ). Ngoài ra, các giá trị trả về chồng lên trong các cuộc gọi lồng nhau: func3 có thể trả về -1, func2 gọi func3, trả về -2 trên các lỗi của anh ta, -1 cho func3's, v.v. - abourget
Tôi đã bỏ phiếu, nhưng tôi đã đảo ngược điều đó bởi vì đây là lý do tại sao ngoại lệ được xem xét. Tuy nhiên theo ý kiến ​​của tôi, hầu như bất kỳ phương pháp hoặc đoạn mã nào đều có thể bị lỗi. Bạn không thể xử lý từng điều kiện lỗi bằng cách giới thiệu một giá trị trả lại cho nó. Bạn sẽ mất thông tin về lỗi. Nghĩ rằng bạn có thể giữ cho mọi thứ được đồng bộ hóa độc đáo bằng cách kiểm tra từng câu lệnh và làm việc dọn dẹp dẫn đến mã rất phức tạp - bắt lỗi trên nhiều câu lệnh và dọn sạch một hoặc hai tài nguyên không phải GC'ed. - Maarten Bodewes


Lý do cho việc không có ngoại lệ được giải thích trong câu hỏi thường gặp về thiết kế ngôn ngữ Go:

Ngoại lệ là một câu chuyện tương tự. A   số lượng thiết kế cho trường hợp ngoại lệ   đã được đề xuất nhưng mỗi bổ sung   sự phức tạp đáng kể đối với ngôn ngữ   và thời gian chạy. Bởi bản chất của họ,   ngoại lệ chức năng và có lẽ   thậm chí goroutines; họ có   các hàm ý rộng. Có   cũng lo ngại về hiệu quả của chúng   sẽ có trên các thư viện. Họ đang,   theo định nghĩa, đặc biệt   trải nghiệm với các ngôn ngữ khác   hỗ trợ họ cho thấy họ có sâu sắc   hiệu ứng trên thư viện và giao diện   đặc điểm kỹ thuật. Nó sẽ rất tuyệt   tìm một thiết kế cho phép họ   thực sự đặc biệt mà không khuyến khích   các lỗi phổ biến để biến thành đặc biệt   luồng kiểm soát yêu cầu mọi   lập trình để bù đắp.

Giống như generics, ngoại lệ vẫn là một   vấn đề mở.

Nói cách khác, họ vẫn chưa tìm ra cách hỗ trợ ngoại lệ trong Go theo cách mà họ nghĩ là thỏa đáng. Họ không nói rằng Ngoại lệ là xấu mỗi gia nhập;

CẬP NHẬT - Tháng 5 năm 2012

Các nhà thiết kế Go giờ đã trèo xuống hàng rào. Câu hỏi thường gặp của họ bây giờ nói điều này:

Chúng tôi tin rằng ngoại lệ khớp nối với cấu trúc điều khiển, như trong thành ngữ cố gắng nắm bắt, kết quả là mã phức tạp. Nó cũng có xu hướng khuyến khích các lập trình viên ghi nhãn quá nhiều lỗi thông thường, chẳng hạn như không mở một tệp, như là ngoại lệ.

Go có cách tiếp cận khác. Để xử lý lỗi đơn giản, trả về nhiều giá trị của Go giúp dễ dàng báo cáo lỗi mà không làm quá tải giá trị trả về. Loại lỗi kinh điển, cùng với các tính năng khác của Go, khiến cho việc xử lý lỗi dễ chịu nhưng khác với các ngôn ngữ khác.

Go cũng có một vài chức năng tích hợp để báo hiệu và phục hồi từ những điều kiện thật sự đặc biệt. Cơ chế khôi phục được thực thi chỉ như một phần của trạng thái của hàm bị rách sau khi lỗi, đủ để xử lý thảm họa nhưng không yêu cầu cấu trúc điều khiển bổ sung và khi sử dụng tốt, có thể dẫn đến mã xử lý lỗi sạch.

Xem bài viết Trì hoãn, Hoảng sợ và Phục hồi để biết chi tiết.

Vì vậy, câu trả lời ngắn gọn là họ có thể làm điều đó khác nhau bằng cách sử dụng trả lại nhiều giá trị. (Và họ cũng có một hình thức xử lý ngoại lệ.)


... và Linus của Linux nổi tiếng đã được gọi là ngoại lệ crap.

Nếu bạn muốn biết tại sao Linus nghĩ rằng ngoại lệ là crap, điều tốt nhất là tìm kiếm các bài viết của mình về chủ đề này. Điều duy nhất tôi đã theo dõi cho đến nay là báo giá này được nhúng vào một vài email trên C ++:

"Toàn bộ điều kiện xử lý ngoại lệ của C ++ bị hỏng về cơ bản. Nó đặc biệt bị hỏng cho nhân."

Bạn sẽ lưu ý rằng anh ta nói về ngoại lệ C ++ nói riêng, và không phải ngoại lệ nói chung. (Và ngoại lệ C ++ làm dường như có một số vấn đề khiến họ khó sử dụng đúng cách.)

Kết luận của tôi là Linus đã không gọi là ngoại lệ (nói chung) "crap" ở tất cả!


47
2017-11-15 02:43



Tôi ghét nó khi họ ẩn thông tin bằng cách đặt nó vào FAQ. :) - brian d foy
Lưu ý rằng Linus bắt đầu email đó với "C ++ là một ngôn ngữ khủng khiếp". và tiếp tục rant về bao nhiêu ông ghét cả C + + và những người chọn để chương trình trong đó. Do đó tôi không nghĩ rằng ý kiến ​​của ông về ngoại lệ của C ++ có thể được coi là đáng tin cậy cho ý kiến ​​hơi thiên vị của ông đối với C ++. - Pharap
@ Hi-Angel - Như văn bản tôi trích dẫn nói: "Để xử lý lỗi đơn giản, trả về nhiều giá trị của Go giúp dễ dàng báo cáo lỗi mà không làm quá tải giá trị trả về".. Đó là cách nó liên quan. Dù bằng cách nào, tôi đang trích dẫn lý do của nhà thiết kế Go. Nếu bạn muốn tranh luận, tranh luận với >> họ <<. - Stephen C
@ Hi-Angel - Tôi không viết những đoạn văn đó. Tôi chỉ trích dẫn họ. Nếu bạn có một vấn đề với họ, bạn có lẽ nên dùng nó với các tác giả. Về lý thuyết tôi có thể đã cung cấp một hướng dẫn về ngôn ngữ Go như ngữ cảnh. Nhưng để thẳng thắn, những người không hiểu thuật ngữ Go có thể truy cập trang web Go để tìm hiểu ý nghĩa của nó. - Stephen C
".. kết quả trong mã phức tạp" Thời gian dài trước đây tôi đã viết chương trình C với nhiều hoạt động chuỗi. Mỗi phương thức đều được cấp phát bộ nhớ và sau đó kiểm tra xem phân bổ có thành công hay không. Có vẻ như đa số mã chỉ kiểm tra phân bổ. Không phải là nó phức tạp? C ++ với ngoại lệ là một giải cứu tuyệt vời cho tôi. - robsosno


Ngoại lệ không phải là xấu, nhưng nếu bạn biết họ sẽ xảy ra rất nhiều, họ có thể tốn kém về hiệu suất.

Quy tắc chung là các ngoại lệ nên gắn cờ các điều kiện đặc biệt và bạn không nên sử dụng chúng để kiểm soát luồng chương trình.


28
2017-11-15 00:41



@ Robert: "bạn không nên sử dụng chúng để kiểm soát dòng chảy chương trình", tôi đã không nghĩ về nó theo cách này, quan điểm mới cho tôi: P +1 - o.k.w
Nó cũng thực sự phụ thuộc vào ngôn ngữ. Thật khó để tránh ngoại lệ nếu bạn đang lập trình Java chẳng hạn. - Charles Salvia
@Charles: Tôi nghĩ rằng điểm là ngoại lệ thích hợp trong các tình huống cho biết lỗi, hệ thống bị định cấu hình sai hoặc đầu vào không hợp lý. Hầu hết các ngoại lệ của thư viện Java có thể tránh được trong mã "quy trình làm việc bình thường". - Artelius
họ không phải trả nhiều tiền. ví dụ, bạn có thể thực hiện một "thử" mà tốn thời gian thực hiện không, và có "ném" tra cứu xử lý ngoại lệ trong một bảng dựa trên địa chỉ người gọi nó thấy trên ngăn xếp ... Tôi sẽ nói rằng những lý do lớn nhất không sử dụng ngoại lệ không liên quan đến hiệu suất. - asveikau
Rephrased; câu hỏi rõ ràng gợi ý về việc sử dụng các ngoại lệ nói chung, hoặc không sử dụng ngoại lệ nào cả (chúng là crap hoặc thậm chí không có trong ngôn ngữ). Câu trả lời của bạn chỉ cho thấy lý do tại sao ngoại lệ là xấu cho hiệu suất khi được sử dụng cho dòng điều khiển chương trình. - Maarten Bodewes


Tôi không đồng ý với "chỉ ném ngoại lệ trong một tình huống đặc biệt." Trong khi nói chung là đúng, nó gây hiểu lầm. Ngoại lệ dành cho lỗi điều kiện (lỗi thực hiện).

Bất kể ngôn ngữ bạn sử dụng, hãy lấy một bản sao của Hướng dẫn thiết kế khung: Công ước, Thành ngữ và Mẫu cho Thư viện .NET có thể Tái sử dụng (Phiên bản 2). Chương về ném ngoại lệ là không có đồng đẳng. Một số trích dẫn từ ấn bản đầu tiên (ấn phẩm thứ 2 tại nơi làm việc của tôi):

  • ĐỪNG trả về mã lỗi.
  • Mã lỗi có thể dễ dàng bị bỏ qua và thường là.
  • Ngoại lệ là phương tiện báo cáo lỗi chính trong các khung công tác.
  • Một nguyên tắc chung là nếu một phương pháp không làm những gì tên của nó gợi ý, nó sẽ được coi là một lỗi mức phương pháp, dẫn đến một ngoại lệ.
  • ĐỪNG sử dụng ngoại lệ cho luồng kiểm soát thông thường, nếu có thể.

Có các trang ghi chú về lợi ích của ngoại lệ (tính nhất quán API, lựa chọn vị trí mã xử lý lỗi, độ mạnh được cải thiện, v.v.) Có phần về hiệu suất bao gồm một số mẫu (Tester-Doer, Try-Parse).

Ngoại lệ và xử lý ngoại lệ không phải xấu. Giống như bất kỳ tính năng nào khác, chúng có thể bị lạm dụng.


23
2017-11-15 03:41



Tôi phải không đồng ý về điều đó. Tôi không chống lại trường hợp ngoại lệ, và cuốn sách đó là phải có, tuy nhiên nó thiên về hướng phát triển .NET và C #. - Ion Todirel
Tôi biết điều này là cổ đại, chỉ muốn bình luận rằng có vẻ như có một sự bất đồng phong cách chung giữa loại .NET và kiểu * nix. Tất cả các thư viện tôi đã sử dụng như là một dev dev sử dụng mã Linux và các hướng dẫn kiểu * nix tôi đã đọc (như công ty của tôi và Google ví dụ) chỉ cần nói "chúng tôi không làm ngoại lệ". Chỉ cần nghĩ rằng nó thú vị. - jarvisteve
Khung nên xử lý ngoại lệ khác với ứng dụng người dùng cuối. Các khung công tác không có cách để giải quyết các lỗi khác ngoài việc ném một ngoại lệ, các ứng dụng của người tiêu dùng sẽ làm. - Mr D


Từ quan điểm của golang, tôi đoán không có xử lý ngoại lệ sẽ giữ cho quá trình biên dịch đơn giản và an toàn.

Từ quan điểm của Linus, tôi hiểu rằng mã hạt nhân là TẤT CẢ về các trường hợp góc. Vì vậy, nó có ý nghĩa để từ chối ngoại lệ.

Trường hợp ngoại lệ có ý nghĩa trong mã là nó được okay để thả nhiệm vụ hiện tại trên sàn nhà, và nơi mã trường hợp phổ biến có tầm quan trọng hơn xử lý lỗi. Nhưng chúng yêu cầu tạo mã từ trình biên dịch.

Ví dụ: chúng tốt ở hầu hết các mã cấp cao, hướng đến người dùng, chẳng hạn như mã ứng dụng web và máy tính để bàn.


11
2017-11-15 00:53



Nhưng điều gì là đúng đối với mã hạt nhân cũng đúng đối với các quy trình máy chủ gốc đang chạy dài. - Lothar


Các đối số tiêu biểu là không có cách nào để nói những ngoại lệ nào sẽ xuất phát từ một đoạn mã cụ thể (tùy thuộc vào ngôn ngữ) và chúng quá giống gotos, làm cho nó khó khăn để theo dõi tinh thần thực hiện.

http://www.joelonsoftware.com/items/2003/10/13.html

Chắc chắn không có sự đồng thuận về vấn đề này. Tôi sẽ nói rằng từ quan điểm của một lập trình viên C lõi cứng như Linus, ngoại lệ chắc chắn là một ý tưởng tồi. Tuy nhiên, một lập trình viên Java điển hình trong một tình huống rất khác.


9
2017-11-15 00:43



Mã C có các ngoại lệ, chỉ theo một cách khác. Bạn cần phải bao bọc tất cả các cuộc gọi đến một chức năng không tầm thường trong ifs mà làm cho việc sử dụng ngôn ngữ đó là một nhức đầu! - RCIX
Ngoài ra còn có setjmp/longjmp thứ khá tệ. - Tim Sylvester
Bạn có thực sự muốn lấy lời khuyên của một người đàn ông coi trọng những người lập trình Duct Tape và ai không tin rằng các bài kiểm tra đơn vị là cần thiết? joelonsoftware.com/items/2009/09/23.html - TrueWill
Đây là sai lầm cổ điển (hoặc lừa đảo) trong một cuộc thảo luận nơi các đối số về chủ đề được thay thế bằng các tham chiếu về tính cách. Đó thường là dấu hiệu của cuộc thảo luận thoái hóa. - Petr Gladkikh
@PetrGladkikh Thảo luận đã bắt đầu với OP đề cập đến ý kiến ​​của Linus ... rằng gian lận được gọi là sự sai lầm của sự hấp dẫn quyền lực. Các cuộc thảo luận chỉ có thể đi lên dốc từ đó, và nó không có "gian lận" để trả lời câu hỏi về lý do tại sao Linus không thích trường hợp ngoại lệ bằng cách đề cập đến tính cách của mình. - Jim Balter


Ngoại lệ trong và của bản thân họ không phải là "xấu", đó là cách mà các ngoại lệ đôi khi được xử lý có xu hướng xấu. Có một số nguyên tắc có thể được áp dụng khi xử lý các ngoại lệ để giúp giảm bớt một số vấn đề này. Một số trong số này bao gồm (nhưng chắc chắn không giới hạn):

  1. Không sử dụng ngoại lệ để kiểm soát luồng chương trình - tức là không dựa vào câu lệnh "bắt" để thay đổi luồng logic. Điều này không chỉ có xu hướng che giấu các chi tiết khác nhau xung quanh logic, nó có thể dẫn đến hiệu suất kém.
  2. Không ném ngoại lệ từ bên trong một hàm khi trạng thái "trả về" sẽ có ý nghĩa hơn - chỉ ném ngoại lệ trong một tình huống đặc biệt. Tạo ngoại lệ là một hoạt động tốn kém, hiệu suất cao. Ví dụ: nếu bạn gọi một phương thức để mở tệp và tệp đó không tồn tại, hãy ném ngoại lệ "FileNotFound". Nếu bạn gọi một phương thức xác định liệu tài khoản khách hàng có tồn tại hay không, hãy trả lại giá trị boolean, không trả về ngoại lệ "CustomerNotFound".
  3. Khi xác định có hay không xử lý một ngoại lệ, không sử dụng mệnh đề "try ... catch" trừ khi bạn có thể làm điều gì đó hữu ích với ngoại lệ. Nếu bạn không thể xử lý ngoại lệ, bạn chỉ nên để nó bong bóng lên ngăn xếp cuộc gọi. Nếu không, các trường hợp ngoại lệ có thể bị xử lý bởi người xử lý và các chi tiết sẽ bị mất (trừ khi bạn rút lại ngoại lệ).

9
2017-11-15 02:22



Trạng thái trở lại là một điều khó khăn. Tôi đã nhìn thấy quá nhiều mã mà có một phương pháp GetCustomer trả về một thực thể khách hàng về thành công hoặc null trên thất bại. Trong nhiều trường hợp mã gọi không bao giờ kiểm tra kết quả, nhưng ngay lập tức truy cập vào Khách hàng. Điều này làm việc hầu hết thời gian ... - TrueWill
Nhưng nếu GetCustomer ném một ngoại lệ thay vì trả về null, mã máy khách vẫn cần xử lý ngoại lệ. Cho dù đó là bằng cách kiểm tra null hoặc bằng cách xử lý các ngoại lệ, trách nhiệm nằm ở mã máy khách - nếu nó không làm điều gì đúng, thì dù sao thì sớm hay muộn cái gì đó sẽ nổ tung. - Chris
@TrueWill Các ngôn ngữ hỗ trợ các mẫu / generics giải quyết vấn đề này bằng cách trả về Option<T> thay vì null ngày nay. Chỉ cần được giới thiệu trong Java 8 ví dụ, lấy một gợi ý từ Guava (và những người khác). - Maarten Bodewes
@owlstead Vâng. Tình yêu có lẽ là monad. Đó là một cách hay để sử dụng nếu ngôn ngữ của bạn hỗ trợ và cung cấp tính năng khớp mẫu. - TrueWill
@owlstead Một lần nữa, những gì Chris nói vẫn giữ vững cho điều đó - người dùng phải nhớ gọi hàm .orElse (defaultObject), hoặc bất kỳ thành ngữ nào trong ngôn ngữ được đề cập đã quyết định. Vào cuối ngày, cuối cùng các lập trình viên là vấn đề, chứ không phải là phương pháp xử lý lỗi. - Pharap


Ngoại lệ không tệ. Chúng phù hợp với mô hình RAII của C ++, đó là điều thanh lịch nhất về C ++. Nếu bạn có một loạt các mã đã được đó không phải là ngoại lệ an toàn, sau đó họ đang xấu trong bối cảnh đó. Nếu bạn đang viết phần mềm cấp thấp thực sự, như hệ điều hành Linux, thì chúng tệ. Nếu bạn thích xả rác mã của bạn với một loạt các kiểm tra lỗi trở lại, sau đó họ không hữu ích. Nếu bạn không có một kế hoạch kiểm soát tài nguyên khi một ngoại lệ được ném (mà C ++ destructors cung cấp) thì chúng xấu.


7
2017-11-15 00:49



RAII rất hữu ích ngay cả khi không có ngoại lệ. - Mark Ransom
tuy nhiên, các ngoại lệ không hữu ích nếu không có RAII (hoặc một số quản lý tài nguyên tự động khác). - Greg Rogers
1 để chỉ ra các tình huống mà các ngoại lệ không phù hợp và ngoại lệ đó không phải là xấu. - Pharap


Một trường hợp sử dụng tuyệt vời cho trường hợp ngoại lệ là như vậy ....

Giả sử bạn đang ở trên một dự án và mọi bộ điều khiển (khoảng 20 dự án chính khác nhau) mở rộng một bộ điều khiển siêu lớp đơn với một phương thức hành động. Sau đó, mỗi bộ điều khiển thực hiện một loạt các công cụ khác nhau từ mỗi đối tượng gọi khác B, C, D trong một trường hợp và F, G, D trong trường hợp khác. Trường hợp ngoại lệ đến để giải cứu ở đây trong nhiều trường hợp, nơi có tấn mã trả về và bộ điều khiển MỌI xử lý nó một cách khác nhau. Tôi whacked tất cả các mã, ném ngoại lệ thích hợp từ "D", bắt nó trong phương pháp điều khiển hành động superclass và bây giờ tất cả các bộ điều khiển của chúng tôi là nhất quán. Trước đây D đã trả về null cho nhiều trường hợp lỗi khác nhau mà chúng tôi muốn thông báo cho người dùng cuối nhưng không thể và tôi không muốn biến StreamResponse thành đối tượng ErrorOrStreamResponse khó chịu (trộn cấu trúc dữ liệu có lỗi theo ý kiến ​​của tôi là một mùi hôi và tôi thấy rất nhiều mã trả về "Luồng" hoặc loại thực thể khác có thông tin lỗi được nhúng trong đó (nó thực sự là hàm trả về cấu trúc thành công HOẶC cấu trúc lỗi mà tôi có thể làm với ngoại lệ so với mã trả về ) ... mặc dù cách C # của nhiều câu trả lời là điều tôi có thể cân nhắc trong nhiều trường hợp, ngoại lệ có thể bỏ qua rất nhiều lớp (các lớp mà tôi không cần phải dọn sạch tài nguyên).

có, chúng ta phải lo lắng về mỗi cấp độ và bất kỳ tài nguyên dọn dẹp / rò rỉ nhưng nói chung không có bộ điều khiển của chúng tôi có bất kỳ nguồn lực để làm sạch sau.

cảm ơn thần chúng tôi đã có ngoại lệ hoặc tôi đã có được trong một refactor rất lớn và lãng phí quá nhiều thời gian vào một cái gì đó mà phải là một vấn đề lập trình đơn giản.


3
2017-07-03 19:38



+1 Dễ dàng một trong những đối số tốt nhất để sử dụng các ngoại lệ mà tôi đã đọc. Có thể đã sử dụng các ví dụ chi tiết hơn (tức là một biểu đồ UML, một số mã giả hoặc một số mã thực tế) nhưng điểm về việc tạo các lớp con thực hiện nhất quán là một ví dụ tốt. Ngoài ra, thực tế bằng chứng của bạn là giai thoại cho thấy rằng ngoại lệ rất hữu ích trong các tình huống thực tế và tính hữu dụng là mục đích chính của bất kỳ tính năng ngôn ngữ nào. - Pharap
cũng giống như một sự bổ sung, nếu bạn đang ở trong scala, bạn có thể quay trở lại Try [ResponseType] đại diện cho một ngoại lệ hoặc phản ứng thực tế. Sau đó bạn có thể làm theo cùng một mẫu mà tôi lảng tránh ở trên mà không có các ngoại lệ thực tế khác ngoài chúng được đưa vào thử. Sau đó, nó giống như mọi phương pháp bạn có trả về 1 + n loại phản ứng mà tôi nghĩ là cần thiết. Tuy nhiên chúng tôi trong scala trở lại tương lai [phản ứng] mà làm việc giống như thử nhưng có thể giúp với nhiều lập trình không đồng bộ. - Dean Hiller


Về mặt lý thuyết chúng thực sự xấu. Trong thế giới toán học hoàn hảo, bạn không thể có được các tình huống ngoại lệ. Nhìn vào các ngôn ngữ chức năng, chúng không có tác dụng phụ, do đó chúng hầu như không có nguồn gốc cho các tình huống không có ngoại lệ.

Nhưng, thực tế lại là một câu chuyện khác. Chúng tôi luôn có những tình huống "bất ngờ". Đây là lý do tại sao chúng ta cần ngoại lệ.

Tôi nghĩ chúng ta có thể nghĩ về các ngoại lệ như đường cú pháp cho ExceptionSituationObserver. Bạn chỉ nhận được thông báo về ngoại lệ. Chỉ có bấy nhiêu thôi.

Với Go, tôi nghĩ rằng họ sẽ giới thiệu một cái gì đó sẽ đối phó với những tình huống "bất ngờ". Tôi có thể đoán rằng họ sẽ cố gắng để làm cho nó âm thanh ít phá hoại như trường hợp ngoại lệ và nhiều hơn nữa như logic ứng dụng. Nhưng đây chỉ là phỏng đoán của tôi.


2
2017-11-15 01:14



"Hãy nhìn vào các ngôn ngữ chức năng, chúng không có tác dụng phụ, vì vậy chúng hầu như không có nguồn gốc cho các tình huống không có ngoại lệ." Đó là một lời nói quá tổng quát. - Stephen C
Bạn có thể vui lòng giải thích về điều này? - Mike Chaliy
5/0 trong toán học là gì? Arcsin (200)? Sqrt (-1)? Toán có rất nhiều tình huống đặc biệt. - Robert Fraser
Đây không phải là tình huống thực thi ... chúng không có ý nghĩa ... và bởi vì điều đó có thể được thực hiện như ngoại lệ ... nhưng cũng có thể được thực hiện như là các điều kiện tiên quyết .. do đó nó phụ thuộc vào việc thực hiện kỹ thuật. - Mike Chaliy
@MikeChaliy - Tôi xin lỗi, nhưng đó chỉ là ngụy biện. Bạn có thể áp dụng loại lý do đó để nói rằng KHÔNG có trường hợp ngoại lệ trong bất cứ điều gì, EVER. Trong thực tế, các biểu thức toán học không có ý nghĩa (hoặc không có giá trị xác định) là ngoại lệ. Điều này không có nghĩa là họ cần được giải quyết bằng cách ném và bắt ngoại lệ ... nhưng nếu bạn không làm điều đó, bạn cần đặc biệt các giá trị (như Inf và Nan) hoặc các hoạt động trả về nhiều giá trị. Tóm lại, những trường hợp này yêu cầu một loại điều trị đặc biệt. - Stephen C


Mô hình xử lý ngoại lệ của C ++, tạo thành một phần cơ sở cho Java, và ngược lại .net, giới thiệu một số khái niệm tốt, nhưng cũng có một số hạn chế nghiêm trọng. Một trong những ý định thiết kế quan trọng của xử lý ngoại lệ là cho phép các phương thức đảm bảo rằng chúng sẽ thỏa mãn các điều kiện sau của chúng hoặc ném một ngoại lệ, và cũng đảm bảo rằng mọi dọn dẹp cần phải xảy ra trước khi phương thức có thể thoát ra. Thật không may, các mô hình xử lý ngoại lệ của C ++, Java và .net tất cả đều không cung cấp bất kỳ phương tiện tốt để xử lý tình huống mà các yếu tố bất ngờ ngăn cản việc dọn dẹp dự kiến ​​được thực hiện. Điều này có nghĩa là người ta phải có nguy cơ bị mọi thứ dừng lại nếu có điều gì đó bất ngờ xảy ra (cách tiếp cận C ++ để xử lý một ngoại lệ xảy ra trong quá trình mở thư), chấp nhận khả năng một điều kiện không thể giải quyết được do sự cố xảy ra trong quá trình dọn dẹp stack-unwinding sẽ bị nhầm lẫn với một trong đó có thể được giải quyết (và có thể đã có, đã dọn dẹp thành công), hoặc chấp nhận khả năng một vấn đề không thể giải quyết mà dọn dẹp ngăn xếp ngăn xếp gây ra một ngoại lệ thường sẽ được giải quyết, có thể đi không được chú ý là mã xử lý vấn đề thứ hai tuyên bố nó "đã giải quyết".

Ngay cả khi xử lý ngoại lệ nói chung là tốt, nó không phải là không hợp lý để coi là một mô hình xử lý ngoại lệ không thể chấp nhận mà không cung cấp một phương tiện tốt để xử lý các vấn đề xảy ra khi làm sạch sau khi các vấn đề khác. Đó không phải là để nói rằng một khuôn khổ không thể được thiết kế với một mô hình xử lý ngoại lệ có thể đảm bảo hành vi hợp lý ngay cả trong các tình huống đa thất bại, nhưng không có ngôn ngữ hoặc khuôn khổ hàng đầu nào có thể làm như vậy.


1
2017-12-11 00:01