Làm thế nào để ném một ArgumentNullException giúp?


27

Hãy nói rằng tôi có một phương pháp:

public void DoSomething(ISomeInterface someObject)
{
    if(someObject == null) throw new ArgumentNullException("someObject");
    someObject.DoThisOrThat();
}

Tôi đã được đào tạo để tin rằng việc ném lỗi ArgumentNullExceptionlà "chính xác" nhưng lỗi "Tham chiếu đối tượng không được đặt thành phiên bản của đối tượng" có nghĩa là tôi có lỗi.

Tại sao?

Tôi biết rằng nếu tôi đã lưu trữ tham chiếu someObjectvà sử dụng nó sau này, thì tốt hơn hết là kiểm tra tính vô hiệu khi được truyền vào và không thành công sớm. Tuy nhiên, nếu tôi hủy đăng ký nó trên dòng tiếp theo, tại sao chúng ta phải làm kiểm tra? Nó sẽ ném một ngoại lệ theo cách này hay cách khác.

Chỉnh sửa :

Nó chỉ xảy ra với tôi ... có phải nỗi sợ null bị hủy bỏ đến từ một ngôn ngữ như C ++ không kiểm tra cho bạn (tức là nó chỉ cố thực hiện một số phương thức tại vị trí bộ nhớ zero + phương thức bù)?


Làm cho ngoại lệ rõ ràng giúp khẳng định rằng có khả năng xảy ra lỗi, có thể được ghi chú trực tiếp trong tài liệu.
zzzzBov

Câu trả lời:


34

Tôi cho rằng nếu bạn ngay lập tức hủy bỏ biến, bạn có thể tranh luận theo bất kỳ cách nào, nhưng tôi vẫn thích ArgumentNullException hơn.
Nó rõ ràng hơn nhiều về những gì đang xảy ra. Ngoại lệ chứa tên của biến là null, trong khi NullReferenceException thì không. Đặc biệt ở cấp API, một ArgumentNullException cho thấy rõ rằng một số người gọi đã không tôn trọng hợp đồng của một phương thức và lỗi không nhất thiết là một lỗi ngẫu nhiên hoặc một số vấn đề sâu hơn (mặc dù đó vẫn có thể là trường hợp). Bạn nên thất bại sớm.

Ngoài ra, những người bảo trì sau này có nhiều khả năng làm gì? Thêm ArgumentNullException nếu họ thêm mã trước lần hủy đăng ký đầu tiên, hoặc chỉ cần thêm mã và sau đó cho phép ném NullReferenceException?


Theo tiện ích mở rộng, nếu đây là phương pháp không công khai hoặc phương thức công khai trên lớp không công khai, bạn không nghĩ có lý do chính đáng nào cho việc kiểm tra null?
Scott Whitlock

Tôi thường không thêm các kiểm tra này vào các phương thức riêng tư, nhưng tôi vẫn có thể thêm chúng vào các phương thức công khai của các lớp riêng để thống nhất. Đối với các phương thức riêng tư, tôi có xu hướng đưa ra một giả định rằng các đối số được xác thực trước đó ở một số mức gọi công khai.
Matt H

54

Tôi đã được đào tạo để tin rằng việc ném ArgumentNullException là "chính xác" nhưng lỗi "Tham chiếu đối tượng không được đặt thành phiên bản của đối tượng" có nghĩa là tôi có lỗi. Tại sao?

Giả sử tôi gọi phương thức M(x)mà bạn đã viết. Tôi vượt qua null. Tôi nhận được một ArgumentNullException với tên được đặt thành "x". Ngoại lệ đó rõ ràng có nghĩa là tôi có một lỗi; Tôi không nên thông qua null cho x.

Giả sử tôi gọi một phương thức M mà bạn đã viết. Tôi vượt qua null. Tôi nhận được một ngoại lệ deref null. Làm thế quái nào tôi có thể biết tôi có lỗi hay bạn có lỗi hoặc thư viện bên thứ ba nào đó mà bạn gọi thay mặt tôi có lỗi không? Tôi không biết gì về việc tôi có cần sửa mã của mình hay gọi cho bạn để bảo bạn sửa mã của bạn.

Cả hai trường hợp ngoại lệ là dấu hiệu của lỗi; không nên bao giờ được ném vào mã sản xuất. Câu hỏi đặt ra là ngoại lệ nào truyền đạt ai có lỗi tốt hơn cho người thực hiện gỡ lỗi? Ngoại lệ Null deref cho bạn gần như không có gì; đối số null ngoại lệ cho bạn rất nhiều. Hãy tử tế với người dùng của bạn; ném ngoại lệ cho phép họ học hỏi từ những sai lầm của họ.


Tôi không chắc chắn tôi hiểu "neither should ever be thrown in production code"loại mã / tình huống nào khác sẽ được sử dụng? Có nên biên dịch ra như thế Debug.Assertnào? Hay bạn có nghĩa là không ném chúng ra khỏi API?
Người sử dụng Stuper

6
@StuperUser: Tôi nghĩ bạn đã hiểu nhầm ý tôi, vì tôi đã viết nó một cách khó hiểu. Bạn nênthrow new ArgumentNullExceptiontrong mã sản xuất của mình và nó không bao giờ nên chạy bên ngoài trường hợp thử nghiệm . Ngoại lệ không bao giờ thực sự được ném bởi vì người gọi không bao giờ có lỗi đó.
Eric Lippert

1
Không chỉ nó giao tiếp người có lỗi, nó giao tiếp nơi lỗi này là. Ngay cả khi cả hai khu vực là của tôi, không phải lúc nào cũng dễ hiểu chính xác biến nào là null.
Ohad Schneider

5

Vấn đề là, mã của bạn nên làm một cái gì đó có ý nghĩa.

A NullReferenceExceptionkhông đặc biệt có ý nghĩa, bởi vì nó có thể có nghĩa là về mọi thứ. Không nhìn vào nơi ngoại lệ được ném ra (và mã có thể không có sẵn cho tôi) Tôi không thể hiểu được, điều gì đã xảy ra.

An ArgumentNullExceptioncó ý nghĩa, bởi vì nó cho tôi biết: hoạt động thất bại vì đối số someObjectnull. Tôi không cần phải nhìn vào mã của bạn để tìm ra điều đó.

Ngoài ra, việc nêu ra ngoại lệ này có nghĩa là bạn xử lý một cách rõ ràng null, mặc dù cách duy nhất để làm điều đó là nói rằng bạn không thể xử lý nó, trong khi chỉ để sự cố mã có thể có nghĩa là, bạn thực sự có thể xử lý null , nhưng quên thực hiện vụ án.

Điểm của các trường hợp ngoại lệ là truyền đạt thông tin trong trường hợp thất bại, có thể được xử lý trong thời gian chạy để phục hồi từ thất bại hoặc tại thời điểm gỡ lỗi để hiểu rõ hơn những gì đã sai.


2

Tôi tin rằng ưu điểm duy nhất là bạn có thể cung cấp chẩn đoán tốt hơn trong trường hợp ngoại lệ. Đó là bạn biết rằng, người gọi hàm đang truyền null, trong khi nếu bạn để cho phép trình duyệt ném nó, bạn sẽ không chắc chắn liệu người gọi có truyền dữ liệu sai hay có lỗi trong chính hàm đó không.

Tôi sẽ làm điều đó nếu ai đó sẽ gọi hàm để làm cho nó dễ sử dụng hơn (gỡ lỗi nếu có lỗi) và không làm điều đó trong mã nội bộ của tôi, vì dù sao thời gian chạy sẽ kiểm tra nó.


1

Tôi đã được đào tạo để tin rằng việc ném ArgumentNullException là "chính xác" nhưng lỗi "Tham chiếu đối tượng không được đặt thành phiên bản của đối tượng" có nghĩa là tôi có lỗi.

Bạn có một lỗi nếu mã không hoạt động như thiết kế. An NullReferenceExceptionbị hệ thống ném và thực tế đó thực sự gây ra nhiều lỗi hơn là một tính năng. Không chỉ bởi vì bạn không xác định ngoại lệ cụ thể sẽ được xử lý. Ngoài ra bởi vì nhà phát triển đọc mã của bạn có thể cho rằng bạn không tính đến tình huống xảy ra.

Vì những lý do tương tự mà ApplicationExceptionthuộc về ứng dụng của bạn so với chung Exceptionthuộc về hệ điều hành. Loại ngoại lệ được ném cho biết ngoại lệ bắt nguồn từ đâu.

Nếu bạn đã chiếm null, tốt hơn hết là bạn xử lý nó một cách duyên dáng bằng cách ném một ngoại lệ cụ thể hoặc nếu đó là một đầu vào chấp nhận được, thì đừng ném ngoại lệ vì chúng đắt tiền. Xử lý nó bằng cách thực hiện các hoạt động với mặc định tốt hoặc hoàn toàn không hoạt động (ví dụ: không cần cập nhật).

Cuối cùng, đừng bỏ qua khả năng xử lý tình huống của người gọi. Bằng cách cung cấp cho họ một ngoại lệ cụ thể hơn, ArgumentExceptionhọ có thể xây dựng thử / bắt của mình theo cách như vậy để xử lý lỗi này trước mức độ chung hơn NullReferenceExceptioncó thể do bất kỳ lý do nào khác xảy ra ở nơi khác, như lỗi không khởi tạo biến xây dựng.


1

Kiểm tra làm cho nó rõ ràng hơn những gì đang xảy ra, nhưng vấn đề thực sự đi kèm với mã như ví dụ (giả định) này:

public void DoSomething(Foo someOtherObject, ISomeInterface someObject)
{
    someOtherObject.DoThisOrThat(this, someObject);
}

Ở đây có một chuỗi cuộc gọi liên quan và không có kiểm tra null, bạn chỉ thấy lỗi trong someOtherObject chứ không phải là vấn đề lần đầu tiên tự tiết lộ - điều đó có nghĩa là ngay lập tức lãng phí thời gian để gỡ lỗi hoặc diễn giải các ngăn xếp cuộc gọi.

Nói chung, tốt hơn là nhất quán với loại điều này và luôn thêm kiểm tra null - điều này giúp dễ dàng phát hiện ra nếu thiếu một thay vì chọn và chọn trên cơ sở từng trường hợp cụ thể (giả sử rằng bạn biết rằng null là luôn không hợp lệ).


1

Một lý do khác để xem xét là nếu một số xử lý xảy ra trước khi đối tượng null được sử dụng thì sao?

Giả sử bạn có tệp dữ liệu gồm ID công ty được liên kết (Cid) và ID người (Pid). Nó được lưu dưới dạng một luồng các cặp số:

Cid Pid Cid Pid Cid Pid Cid Pid

Và hàm VB này ghi các bản ghi vào tệp:

Sub WriteRecord(Company As CompanyInfo, Person As PersonInfo)
    Using File As New StringWriter(DataFileName)
        File.Write(Company.ID)
        File.Write(Person.ID)
    End Using
End Sub

Nếu Personlà một tham chiếu null, dòng File.Write(Person.ID)sẽ ném một ngoại lệ. Phần mềm có thể bắt ngoại lệ và phục hồi, nhưng điều này để lại một vấn đề. ID công ty đã được viết mà không cần ID người liên quan. Nếu thực tế, khi bạn gọi chức năng này tiếp theo, bạn sẽ đặt ID công ty nơi ID người trước đó được mong đợi. Cũng như hồ sơ đó là không chính xác, mỗi hồ sơ tiếp theo sẽ có một ID người theo sau là ID công ty, đó là cách sai.

Cid Pid Cid Pid Cid Pid Cid Pid Cid

Bằng cách xác nhận các tham số khi bắt đầu chức năng, bạn ngăn chặn mọi xử lý xảy ra khi phương thức được đảm bảo không thành công.

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.