2009-09-23 30 views
78

Tôi đã đọc rất nhiều articles (và một số khác tương tự câu hỏi đã được đăng trên StackOverflow) về cách thức và thời điểm sử dụng xác nhận và tôi hiểu rõ chúng. Tuy nhiên, tôi vẫn không hiểu động lực nào sẽ thúc đẩy tôi sử dụng Debug.Assert thay vì ném một ngoại lệ đơn giản. Những gì tôi có nghĩa là, trong .NET các phản ứng mặc định để một khẳng định không thành công là "ngăn chặn thế giới" và hiển thị một hộp thông báo cho người dùng. Mặc dù loại hành vi này có thể được sửa đổi, tôi thấy nó rất khó chịu và dư thừa để làm điều đó, trong khi tôi có thể thay thế, chỉ cần ném một ngoại lệ phù hợp. Bằng cách này, tôi có thể dễ dàng ghi lỗi vào nhật ký của ứng dụng ngay trước khi tôi ném ngoại lệ, và cộng với, ứng dụng của tôi không nhất thiết phải đóng băng.Debug.Assert vs Exception Throwing

Vì vậy, tại sao tôi nên sử dụng Debug.Assert thay vì ngoại lệ đơn giản? Đặt một khẳng định nơi nó không nên có thể chỉ gây ra tất cả các loại "hành vi không mong muốn", do đó, theo quan điểm của tôi, tôi thực sự không đạt được bất cứ điều gì bằng cách sử dụng một khẳng định thay vì ném một ngoại lệ. Bạn có đồng ý với tôi hay tôi đang thiếu thứ gì đó ở đây?

Lưu ý: Tôi hoàn toàn hiểu sự khác biệt "về mặt lý thuyết" (Debug vs Release, mẫu sử dụng, v.v.), nhưng như tôi thấy, tôi sẽ tốt hơn khi ném một ngoại lệ thay vì thực hiện một khẳng định. Vì nếu một lỗi được phát hiện trên bản phát hành sản phẩm, tôi vẫn muốn rằng "xác nhận" sẽ thất bại (sau khi tất cả, "overhead" là ridiculously nhỏ), vì vậy tôi sẽ tốt hơn off ném một ngoại lệ để thay thế.


Edit: Con đường tôi nhìn thấy nó, nếu một khẳng định thất bại, điều đó có nghĩa rằng việc áp dụng vào một số loại hỏng nhà nước, bất ngờ. Vậy tại sao tôi muốn tiếp tục thực hiện? Nó không quan trọng nếu ứng dụng chạy trên một phiên bản gỡ lỗi hoặc phát hành. Tương tự với cả hai số

+0

Đối với những điều bạn đang nói "nếu một lỗi được phát hiện trên bản phát hành sản xuất, tôi vẫn muốn rằng" xác nhận "sẽ thất bại", ngoại lệ là những gì bạn nên sử dụng –

+0

Hiệu suất là lý do duy nhất. Null kiểm tra tất cả mọi thứ tất cả các thời gian có thể làm giảm tốc độ, mặc dù nó có thể hoàn toàn không đáng kể. Điều này chủ yếu cho các trường hợp không bao giờ nên xảy ra, ví dụ như bạn biết bạn đã kiểm tra nó đã có trong một chức năng trước đó, không có chu kỳ lãng phí điểm kiểm tra nó một lần nữa. Debug.assert hoạt động hiệu quả như một bài kiểm tra đơn vị cơ hội cuối cùng để thông báo cho bạn. – rolls

Trả lời

152

Mặc dù tôi đồng ý rằng lý do của bạn là chính đáng - tức là nếu xác nhận bị vi phạm bất ngờ, có nghĩa là tạm dừng thực thi bằng cách ném - cá nhân tôi sẽ không sử dụng ngoại lệ ở vị trí xác nhận. Dưới đây là lý do:

Như những người khác đã nói, khẳng định nên tài liệu tình huống mà không thể, theo cách như vậy mà nếu tình hình bị cáo buộc không thể đến để vượt qua, các nhà phát triển được thông báo. Ngược lại, các ngoại lệ, cung cấp cơ chế kiểm soát lưu lượng cho các tình huống đặc biệt, khó xảy ra hoặc không đúng, nhưng không phải là tình huống không thể xảy ra. Đối với tôi, điểm khác biệt chính là:

  • Nên luôn luôn có thể tạo ra một trường hợp thử nghiệm thực hiện một tuyên bố ném. Nếu không thể tạo ra một trường hợp thử nghiệm như vậy thì bạn có một đường dẫn mã trong chương trình của bạn mà không bao giờ thực hiện, và nó phải được loại bỏ như là mã chết.

  • KHÔNG BAO GIỜ có thể tạo ra trường hợp thử nghiệm gây ra xác nhận kích hoạt. Nếu một khẳng định cháy, hoặc là mã sai hoặc xác nhận là sai; một trong hai cách, một cái gì đó cần phải thay đổi trong mã.

Đó là lý do tại sao tôi sẽ không thay thế xác nhận có ngoại lệ. Nếu xác nhận không thể thực sự kích hoạt, thì thay thế nó bằng ngoại lệ có nghĩa là bạn có đường dẫn mã không thể đọc được trong chương trình. Tôi không thích đường dẫn mã không thể khắc phục.

+10

Vấn đề với xác nhận là chúng không có trong bản dựng sản phẩm. Không có điều kiện giả định có nghĩa là chương trình của bạn đã nhập vào vùng hành vi không xác định, trong trường hợp đó chương trình chịu trách nhiệm phải tạm dừng thực thi càng sớm càng tốt (thư giãn chồng cũng có phần nguy hiểm, tùy thuộc vào mức độ nghiêm ngặt bạn muốn nhận). Có, các xác nhận thường phải là _impossible_ để kích hoạt, nhưng bạn không biết điều gì có thể xảy ra khi mọi thứ diễn ra trong tự nhiên. Những gì bạn nghĩ là _impossible_ có thể xảy ra trong sản xuất và một chương trình có trách nhiệm sẽ phát hiện các giả định vi phạm và hành động kịp thời. – kizzx2

+2

@ kizzx2: OK, vậy bạn có thể viết bao nhiêu ngoại lệ trên mỗi dòng mã sản xuất? –

+0

@ kizzx2: không chắc chắn nó có thể được thực hiện bằng C# nhưng bằng các ngôn ngữ khác bạn có thể đưa ra quyết định ném ngoại lệ thay vì bị xóa trong mã sản xuất của bạn (tôi sẽ không tranh luận bạn có nên làm điều đó) hay không. –

15

Các xác nhận được sử dụng để kiểm tra sự hiểu biết của người lập trình trên thế giới. Một khẳng định sẽ thất bại chỉ khi lập trình viên đã làm điều gì sai. Ví dụ, không bao giờ sử dụng một xác nhận để kiểm tra đầu vào của người dùng.

Xác nhận thử nghiệm cho các điều kiện "không thể xảy ra". Các trường hợp ngoại lệ dành cho các điều kiện "không nên xảy ra nhưng thực hiện".

Các xác nhận hữu ích vì tại thời gian xây dựng (hoặc thậm chí thời gian chạy), bạn có thể thay đổi hành vi của chúng. Ví dụ, thường trong bản phát hành bản phát hành, các xác nhận thậm chí không được kiểm tra, bởi vì chúng giới thiệu chi phí không cần thiết. Đây cũng là một cái gì đó để được cảnh giác: các xét nghiệm của bạn có thể thậm chí không được thực hiện.

Nếu bạn sử dụng ngoại lệ thay vì khẳng định, bạn sẽ mất một số giá trị:

  1. Mã này là tiết hơn, vì thử nghiệm và ném một ngoại lệ là ít nhất hai dòng, trong khi một khẳng định chỉ là một.

  2. Mã kiểm tra và ném của bạn sẽ luôn chạy, trong khi các xác nhận có thể được biên dịch.

  3. Bạn mất một số liên lạc với các nhà phát triển khác, bởi vì các xác nhận có ý nghĩa khác với mã sản phẩm kiểm tra và ném. Nếu bạn đang thực sự thử nghiệm một xác nhận lập trình, hãy sử dụng một khẳng định.

More đây: http://nedbatchelder.com/text/assert.html

11

EDIT: Để đối phó với các chỉnh sửa/lưu ý bạn đã thực hiện trong bài viết của bạn: Nghe có vẻ như sử dụng ngoại lệ đó là điều đúng đắn nên sử dụng qua sử dụng khẳng định cho loại điều bạn đang cố gắng hoàn thành. Tôi nghĩ rằng những trở ngại tinh thần bạn đang đánh là bạn đang xem xét ngoại lệ và xác nhận để thực hiện cùng một mục đích, và vì vậy bạn đang cố gắng tìm ra cái nào sẽ là 'đúng' để sử dụng. Mặc dù có thể có một số trùng lặp trong cách xác nhận và ngoại lệ có thể được sử dụng, đừng nhầm lẫn rằng chúng là các giải pháp khác nhau cho cùng một vấn đề - chúng không được. Mỗi xác nhận và ngoại lệ đều có mục đích, điểm mạnh và điểm yếu riêng.

tôi sẽ gõ lên một câu trả lời theo cách của tôi nhưng điều này không công bằng khái niệm tốt hơn tôi sẽ có:

C# Station: Assertions

Việc sử dụng các báo cáo khẳng định có thể là một hiệu quả cách để bắt logic chương trình lỗi khi chạy, tuy nhiên chúng là dễ dàng lọc ra khỏi mã sản xuất . Khi phát triển hoàn tất, chi phí thời gian chạy của các kiểm tra lỗi dư thừa này có thể được xóa đơn giản bằng cách xác định biểu tượng tiền xử lý NDEBUG [trong đó vô hiệu hóa tất cả các xác nhận] trong khi biên soạn . Tuy nhiên, hãy đảm bảo rằng mã số được đặt trong số tự khẳng định sẽ bị bỏ qua trong phiên bản sản xuất .

Một khẳng định là tốt nhất dùng để thử nghiệm một điều kiện chỉ khi tất cả các giữ sau:

* the condition should never be false if the code is correct, 
* the condition is not so trivial so as to obviously be always true, and 
* the condition is in some sense internal to a body of software. 

Khẳng định nên hầu như không bao giờ được sử dụng để phát hiện các tình huống phát sinh trong quá trình hoạt động bình thường phần mềm. Ví dụ: thường xác nhận không được sử dụng để kiểm tra lỗi trong đầu vào của người dùng . Tuy nhiên, nó có thể làm cho cảm giác sử dụng các xác nhận để xác minh rằng một người gọi đã kiểm tra đầu vào của người dùng.

Về cơ bản, hãy sử dụng ngoại lệ cho những thứ cần được xử lý trong ứng dụng sản xuất, sử dụng xác nhận để thực hiện kiểm tra lôgic sẽ hữu ích cho phát triển nhưng bị tắt trong quá trình sản xuất.

+0

Tôi nhận ra tất cả điều đó. Nhưng vấn đề là, cùng một tuyên bố mà bạn đánh dấu là đậm cũng đi đến ngoại lệ. Vì vậy, cách tôi nhìn thấy nó, thay vì xác nhận, tôi có thể ném một ngoại lệ (vì nếu "tình huống không bao giờ xảy ra" xảy ra trên một phiên bản được triển khai, tôi vẫn muốn được thông báo về nó [plus, ứng dụng có thể nhập trạng thái bị hỏng, vì vậy tôi ngoại lệ phù hợp, tôi có thể không muốn tiếp tục luồng thực thi thông thường) –

+1

Các xác nhận phải được sử dụng trên các biến thể; ngoại lệ nên được sử dụng khi, nói rằng, một cái gì đó không nên null, nhưng nó sẽ được (như một tham số cho một phương pháp). –

+0

Tôi đoán nó tất cả đi xuống đến cách bảo vệ bạn muốn mã. –

0

Gỡ lỗi.Assert theo mặc định sẽ chỉ hoạt động trong các bản dựng lỗi, vì vậy nếu bạn muốn phát hiện bất kỳ hành vi không mong muốn nào xấu trong bản phát hành của mình, bạn sẽ cần sử dụng ngoại lệ hoặc bật hằng số gỡ lỗi trong thuộc tính dự án của mình (được coi là nói chung không phải là một ý tưởng tốt).

+0

câu đầu tiên là đúng, phần còn lại nói chung là một ý tưởng tồi: xác nhận là giả định và không xác nhận (như đã nêu ở trên), cho phép gỡ lỗi trong bản phát hành thực sự không có tùy chọn. –

+1

Tôi chưa bao giờ nói đó là một ý tưởng tốt .. – Mez

4

Một nugget từ Code Complete:

"Một sự khẳng định là một chức năng hoặc vĩ mô mà phàn nàn ầm ĩ nếu một giả định là không đúng Sử dụng khẳng định tài liệu giả định thực hiện trong mã và để tuôn ra. điều kiện bất ngờ ...

"Trong quá trình phát triển, xác nhận tuôn ra các giả định mâu thuẫn, điều kiện bất ngờ, giá trị xấu được chuyển đến routi nes, v.v. "

Ông tiếp tục thêm một số nguyên tắc về những gì nên và không nên được khẳng định.

Mặt khác, trường hợp ngoại lệ:

"xử lý Sử dụng ngoại lệ để vẽ ý đến trường hợp bất ngờ trường hợp ngoại lệ nên được xử lý trong một cách mà làm cho chúng rõ ràng trong phát triển và thu hồi khi . mã sản xuất đang chạy. "

Nếu bạn không có cuốn sách này, bạn nên mua nó.

+2

Tôi đã đọc cuốn sách, nó rất tuyệt vời. Tuy nhiên .. bạn đã không trả lời câu hỏi của tôi :) –

+0

Bạn nói đúng Tôi đã không trả lời câu hỏi đó. Câu trả lời của tôi là không, tôi không đồng ý với bạn. Các xác nhận và ngoại lệ là các động vật khác nhau như được đề cập ở trên và một số câu trả lời được đăng khác ở đây. –

4

Tôi nghĩ rằng một (giả tạo) ví dụ thực tế có thể giúp làm sáng tỏ sự khác biệt:

(chuyển thể từ MoreLinq's Batch extension)

// 'public facing' method 
public int DoSomething(List<string> stuff, object doohickey, int limit) { 

    // validate user input and report problems externally with exceptions 

    if(stuff == null) throw new ArgumentNullException("stuff"); 
    if(doohickey == null) throw new ArgumentNullException("doohickey"); 
    if(limit <= 0) throw new ArgumentOutOfRangeException("limit", limit, "Should be > 0"); 

    return DoSomethingImpl(stuff, doohickey, limit); 
} 

// 'developer only' method 
private static int DoSomethingImpl(List<string> stuff, object doohickey, int limit) { 

    // validate input that should only come from other programming methods 
    // which we have control over (e.g. we already validated user input in 
    // the calling method above), so anything using this method shouldn't 
    // need to report problems externally, and compilation mode can remove 
    // this "unnecessary" check from production 

    Debug.Assert(stuff != null); 
    Debug.Assert(doohickey != null); 
    Debug.Assert(limit > 0); 

    /* now do the actual work... */ 
} 

Vì vậy, khi Eric Lippert et al đã nói, bạn chỉ khẳng định những thứ mà bạn mong đợi chính xác, chỉ trong trường hợp bạn (nhà phát triển) vô tình sử dụng sai ở một nơi khác, để bạn có thể sửa mã của mình. Về cơ bản, bạn ném ngoại lệ khi bạn không có quyền kiểm soát hoặc không thể lường trước những gì có trong, ví dụ: cho đầu vào của người dùng, sao cho mọi thứ cho dữ liệu xấu có thể phản hồi một cách thích hợp (ví dụ: người dùng).