Tại sao tham chiếu đối tượng không được đặt thành một thể hiện của một đối tượng mà cho chúng ta biết đối tượng nào?


39

Chúng tôi đang khởi chạy một hệ thống và đôi khi chúng tôi nhận được ngoại lệ nổi tiếng NullReferenceExceptionvới thông báo Object reference not set to an instance of an object.

Tuy nhiên, trong một phương thức mà chúng ta có gần 20 đối tượng, có một nhật ký cho biết một đối tượng là null, thực sự không có ích gì cả. Nó giống như nói với bạn, khi bạn là nhân viên an ninh của một cuộc hội thảo, rằng một người đàn ông trong số 100 người tham dự là một kẻ khủng bố. Điều đó thực sự không có ích gì với bạn cả. Bạn nên lấy thêm thông tin, nếu bạn muốn phát hiện người đàn ông nào là người đàn ông đe dọa.

Tương tự như vậy, nếu chúng ta muốn loại bỏ lỗi, chúng ta cần phải biết đối tượng nào là null.

Bây giờ, một cái gì đó đã ám ảnh tâm trí tôi trong vài tháng, và đó là:

Tại sao .NET không cung cấp cho chúng tôi tên, hoặc ít nhất là loại tham chiếu đối tượng, là null? . Nó không thể hiểu loại từ phản ánh hoặc bất kỳ nguồn nào khác?

Ngoài ra, các thực tiễn tốt nhất để hiểu đối tượng nào là null? Chúng ta có nên luôn kiểm tra tính vô hiệu của các đối tượng trong các bối cảnh này bằng tay và ghi lại kết quả không? Có cách nào tốt hơn?

Cập nhật: Ngoại lệ The system cannot find the file specifiedcó cùng bản chất. Bạn không thể tìm thấy tập tin nào, cho đến khi bạn đính kèm vào quy trình và gỡ lỗi. Tôi đoán những loại ngoại lệ này có thể trở nên thông minh hơn. Sẽ không tốt hơn nếu .NET có thể cho chúng ta biết c:\temp.txt doesn't exist.thay vì thông điệp chung đó? Là một nhà phát triển, tôi bỏ phiếu có.


14
Ngoại lệ nên bao gồm một dấu vết ngăn xếp với số dòng. Tôi sẽ bắt đầu cuộc điều tra của mình từ đó xem xét mọi đối tượng truy cập trên dòng đó.
PersonalNexus

2
Ngoài ra, tôi đã luôn tự hỏi tại sao hộp thoại trình trợ giúp ngoại lệ trong Visual Studio bao gồm gợi ý "hữu ích" để sử dụng newđể tạo các thể hiện của một lớp. Khi nào một gợi ý như vậy thực sự giúp đỡ?
PersonalNexus

1
Nếu bạn có một chuỗi mười cuộc gọi đối tượng thì bạn đã gặp vấn đề với khớp nối trong thiết kế của mình.
Pete Kirkham


9
Tôi thích cách mỗi câu trả lời cho câu hỏi này nằm dọc theo dòng chữ "Sử dụng trình gỡ lỗi, ghi lại lỗi của bạn, kiểm tra null, dù sao đó cũng là lỗi của bạn" không trả lời câu hỏi mà đổ lỗi cho bạn. Chỉ trên stackoverflow mới có người thực sự cho bạn câu trả lời (mà tôi nghĩ rằng nó quá nhiều chi phí để VM theo dõi). Nhưng thực sự, những người duy nhất có thể trả lời đúng câu hỏi này là một người nào đó từ microsoft đã làm việc trên khung.
Rocklan

Câu trả lời:


28

Các NullReferenceExceptioncơ bản cho bạn: bạn đang làm nó sai. Không hơn không kém. Nó không phải là một công cụ sửa lỗi đầy đủ, trái lại. Trong trường hợp này tôi sẽ nói bạn làm sai cả hai vì

  • có một ngoại lệ NullReference
  • bạn đã không ngăn chặn nó theo cách bạn biết tại sao / nơi nó xảy ra
  • và cũng có thể: một phương thức yêu cầu 20 đối tượng có vẻ hơi sai

Tôi là một fan hâm mộ lớn của việc kiểm tra mọi thứ trước khi mọi thứ bắt đầu sai và cung cấp thông tin tốt cho nhà phát triển. Tóm lại: viết séc bằng cách sử dụng ArgumentNullExceptionvà thích và tự viết tên. Đây là một mẫu:

void Method(string a, SomeObject b)
{
    if (a == null) throw ArgumentNullException("a");
    if (b == null) throw ArgumentNullException("b");

    // See how nice this is, and what peace of mind this provides? As long as
    // nothing modifies a or b you can use them here and be 100% sure they're not
    // null. Should they be when entering the method, at least you know which one
    // is null.
    var c = FetchSomeObject();
    if(c == null)
    {
        throw InvalidOperationException("Fetching B failed!!");
    }

    // etc.
}

Bạn cũng có thể xem xét Hợp đồng mã , nó có những điều kỳ quặc nhưng nó hoạt động khá tốt và giúp bạn tiết kiệm một số thao tác gõ.


17
@SaeedNeamati bạn không ngụ ý rằng vì bạn có một cơ sở mã lớn nên bạn không nên kiểm tra lỗi đúng không? Imo dự án càng lớn, càng quan trọng trở thành vai trò hoặc kiểm tra lỗi và báo cáo.
Stijn

6
+1 cho lời khuyên tốt, ngay cả khi nó không trả lời câu hỏi thực tế (điều mà có lẽ chỉ có Anders Hejlsberg mới có thể trả lời).
Ross Patterson

12
+1 cho lời khuyên tuyệt vời. @SaeedNeamati bạn nên nghe lời khuyên này. Vấn đề của bạn là do sự bất cẩn và thiếu chuyên nghiệp trong mã. Nếu bạn không có thời gian để viết mã tốt, bạn sẽ gặp vấn đề lớn hơn nhiều ...
MattDavey

3
Nếu bạn không có thời gian để viết mã tốt , thì bạn chắc chắn không có thời gian để viết mã xấu . Viết mã xấu mất nhiều thời gian hơn viết mã tốt. Trừ khi bạn thực sự không quan tâm đến lỗi. Và nếu bạn thực sự không quan tâm đến lỗi, tại sao lại viết chương trình?
MarkJ

5
@SaeedNeamati I only say that checking every object to get sure that it's not null, is not a good methodNghiêm túc. Đó là phương pháp tốt nhất. Và không chỉ null, kiểm tra mọi đối số cho các giá trị hợp lý. Bạn càng bắt lỗi sớm thì càng dễ tìm ra nguyên nhân. Bạn không muốn phải quay lại nhiều cấp độ trong theo dõi ngăn xếp để tìm cuộc gọi gây ra.
jgauffin

19

Nó thực sự sẽ hiển thị chính xác những gì bạn đang cố gắng để gọi. Giống như nói "Có vấn đề

Vì vậy, nó hữu ích như thế nào, ví dụ, nếu bạn có ...

Object reference (HttpContext.Current) not set to instance of an object

...? Để đi vào mã, bước qua nó và tìm ra rằng điều bạn đang cố gắng gọi nulllà ổn, nhưng tại sao không chỉ giúp chúng tôi một chút giúp đỡ?

Tôi đồng ý rằng thường rất hữu ích khi bước qua mã để đi đến câu trả lời (bởi vì bạn có thể sẽ tìm hiểu thêm), nhưng thường sẽ rất nhiều thời gian và sự thất vọng sẽ được lưu lại nếu NullReferenceExceptionvăn bản giống như ví dụ trên.

Chỉ cần nói.


Làm thế nào là thời gian chạy phải biết null là gì?
Sẽ

3
Thời gian chạy có nhiều thông tin hơn nó cung cấp. Bất cứ thông tin hữu ích nào nó có, nó nên cung cấp. Đó là tất cả những gì tôi đang nói. Trả lời câu hỏi của bạn, tại sao nó không biết? Nếu bạn biết câu trả lời mà bạn có thể đưa ra nhận xét tốt hơn bạn có.
LiverpoolsNumber9

Đồng ý, và tôi cũng nói như vậy KeyNotFoundExceptionvà nhiều phiền toái khác ...
sinelaw 19/03/13

Trên thực tế, trong trường hợp hủy bỏ hội thảo null, điều này có vẻ như là một hạn chế trong cách các cuộc hội thảo CLR (nhờ nhận xét của Shahrooz Jefri về câu hỏi của OP)
sinelaw 19/03/13


4

Nhật ký của bạn nên bao gồm một dấu vết ngăn xếp - thường cung cấp cho bạn một gợi ý về dòng nào trong phương thức có vấn đề. Bạn có thể cần phải xây dựng bản phát hành của mình bao gồm các ký hiệu PDB để bạn có ý tưởng về lỗi xảy ra.

Cấp, nó sẽ không giúp bạn trong trường hợp này:

Foo.Bar.Baz.DoSomething()

Các tell đừng hỏi nguyên tắc có thể giúp tránh mã như vậy.

Về lý do tại sao thông tin không được bao gồm, tôi không chắc chắn - tôi nghi ngờ rằng ít nhất là trong một bản dựng gỡ lỗi, nếu họ thực sự muốn, họ có thể tìm ra nó. Nhận một bãi chứa sự cố và mở trong WinDBG có thể giúp đỡ.


2

Các ngoại lệ đã được tạo ra như một công cụ để báo hiệu các điều kiện không gây tử vong đặc biệt lên chuỗi cuộc gọi. Đó là, chúng không được thiết kế như một công cụ gỡ lỗi.

Nếu một ngoại lệ con trỏ null là một công cụ gỡ lỗi, nó sẽ hủy bỏ việc thực thi chương trình ngay tại chỗ, cho phép trình gỡ lỗi kết nối, trỏ nó ngay vào dòng bắt buộc. Điều này sẽ cung cấp cho các lập trình viên tất cả các thông tin bối cảnh có sẵn. (Đó là khá nhiều những gì một Segfault do truy cập con trỏ null thực hiện trong C, mặc dù hơi thô sơ.)

Tuy nhiên, ngoại lệ con trỏ null được thiết kế như một điều kiện thời gian chạy hợp lệ có thể được ném và bắt trong luồng chương trình bình thường. Do đó, các cân nhắc về hiệu suất cần phải được tính đến. Và bất kỳ tùy chỉnh nào của thông báo ngoại lệ đều yêu cầu các đối tượng chuỗi được tạo, nối và hủy khi chạy. Như vậy, một tin nhắn tĩnh là nhanh hơn không thể chối cãi.

Tuy nhiên, tôi không nói rằng thời gian chạy không thể được lập trình theo cách mang lại tên của tài liệu tham khảo bắt buộc. Điều đó có thể được thực hiện. Nó sẽ chỉ làm cho ngoại lệ thậm chí chậm hơn so với họ. Nếu bất cứ ai quan tâm đủ, một tính năng như vậy thậm chí có thể được chuyển đổi, để nó không làm chậm mã sản xuất, nhưng cho phép gỡ lỗi dễ dàng hơn; nhưng vì một số lý do dường như không ai quan tâm đủ.


1

Tôi nghĩ rằng Cromulent đã bắn đinh vào đầu, tuy nhiên cũng có một điểm rõ ràng là nếu bạn nhận được một NullReferenceExceptionbiến số chưa được xác định. Đối số mà bạn có khoảng 20 đối tượng được truyền vào một phương thức có thể được coi là giảm thiểu: vì người tạo ra một đoạn mã bạn phải chịu trách nhiệm về việc đó, bao gồm việc tuân thủ phần còn lại của một cơ sở mã, như cũng như sử dụng đúng và chính xác các biến vv

nó là khó khăn, tẻ nhạt và đôi khi buồn tẻ, nhưng phần thưởng cuối cùng là xứng đáng: nhiều lần tôi đã phải truy tìm các tệp nhật ký có trọng lượng vài gigabyte và chúng hầu như luôn hữu ích. Tuy nhiên trước khi bạn đến giai đoạn đó, trình gỡ lỗi có thể giúp bạn và trước giai đoạn đó, việc lập kế hoạch tốt sẽ tiết kiệm rất nhiều nỗi đau (và tôi không có nghĩa là một cách tiếp cận được thiết kế đầy đủ cho giải pháp mã của bạn: phác thảo đơn giản và một số ghi chú có thể và sẽ tốt hơn không có gì).

Liên quan đến Object reference not set to an instance of an objectmã không thể đoán được các giá trị mà chúng tôi có thể thích: đó là công việc của chúng tôi với tư cách là lập trình viên và nó đơn giản có nghĩa là bạn đã vượt qua một biến chưa được khởi tạo.


Bạn nhận ra mọi người thường đưa ra những lời biện minh như thế này để viết bằng ngôn ngữ lắp ráp? Và không sử dụng thu gom rác tự động? Và có thể làm số học - trong hex? "Khó chịu, tẻ nhạt và đôi khi buồn tẻ" là cách chúng tôi mô tả các công việc nên được tự động hóa.
Spike0xff

0

Tìm hiểu để sử dụng trình gỡ lỗi. Đây chính xác là loại mà nó được thiết kế cho. Đặt điểm dừng trên phương thức đang đề cập và bạn sẽ đi.

Chỉ cần bước qua mã của bạn và xem chính xác giá trị của tất cả các biến của bạn tại các điểm nhất định.

Chỉnh sửa: Thành thật mà nói tôi bị sốc khi chưa có ai khác đề cập đến việc sử dụng trình gỡ lỗi.


2
Vì vậy, bạn đang nói rằng tất cả các ngoại lệ có thể dễ dàng được sao chép khi sử dụng trình gỡ lỗi?
jgauffin

@jgauffin Tôi đang nói rằng bạn có thể sử dụng trình gỡ lỗi để xem lý do tại sao một ngoại lệ được ném vào mã thế giới thực chứ không phải kiểm tra đơn vị tổng hợp có thể không thực sự kiểm tra đầy đủ mã trong câu hỏi hoặc bản thân đơn vị kiểm tra có thể có lỗi trong đó gây ra lỗi họ bỏ lỡ lỗi trong mã thực. Một trình gỡ lỗi chỉ hơn bất kỳ công cụ nào khác mà tôi có thể nghĩ ra (ngoại trừ những thứ như Valgrind hoặc DTrace).
Cromulent

1
Vì vậy, bạn đang nói rằng chúng tôi luôn có quyền truy cập vào máy tính bị lỗi và gỡ lỗi là tốt hơn so với kiểm tra đơn vị?
jgauffin

@jgauffin Tôi đang nói nếu bạn có sự lựa chọn thì hãy đi với trình gỡ lỗi để ưu tiên cho các công cụ khác. Tất nhiên, nếu bạn không có lựa chọn đó thì đó là một chút không bắt đầu. Rõ ràng đây không phải là trường hợp với câu hỏi này, đó là lý do tại sao tôi đã đưa ra câu trả lời tôi đã làm. Nếu câu hỏi là làm thế nào để bạn khắc phục vấn đề này trên máy khách mà không có cách nào để gỡ lỗi từ xa (hoặc gỡ lỗi cục bộ) thì câu trả lời của tôi sẽ khác. Bạn dường như đang cố gắng để làm cong câu trả lời của tôi theo những cách không liên quan đến câu hỏi trong tay.
Cromulent

0

Nếu bạn muốn đi với câu trả lời của @ stijn và đặt kiểm tra null vào mã của bạn, đoạn mã này sẽ giúp ích. Dưới đây là một số thông tin về đoạn mã . Khi bạn đã thiết lập xong, chỉ cần gõ argnull, nhấn tab hai lần, sau đó điền vào chỗ trống.

<CodeSnippet Format="1.0.0">
  <Header>
    <Title>EnsureArgNotNull</Title>
    <Shortcut>argnull</Shortcut>
  </Header>
  <Snippet>
    <Declarations>
      <Literal>
        <ID>argument</ID>
        <ToolTip>The name of the argument that shouldn't be null</ToolTip>
        <Default>arg</Default>
      </Literal>
    </Declarations>
    <Code Language="CSharp">
      <![CDATA[if ($argument$ == null) throw new ArgumentNullException("$argument$");$end$]]>
    </Code>
  </Snippet>
</CodeSnippet>

lưu ý: c # 6 hiện có nameofvì vậy đoạn trích của bạn có thể throw new ArgumentNullException(nameof($argument$))có ưu điểm là không bao gồm các hằng số ma thuật, được trình biên dịch kiểm tra và hoạt động tốt hơn với các công cụ tái cấu trúc
stijn
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.