Tham chiếu đối tượng không được đặt thành một thể hiện của đối tượng. Tại sao .NET không hiển thị đối tượng nào là `null`?


103

Về thông báo ngoại lệ chưa được xử lý .NET này:

Tham chiếu đối tượng không được đặt thành một phiên bản của đối tượng.

Tại sao .NET không hiển thị đối tượng là null?

Tôi biết rằng tôi có thể kiểm tra nullvà giải quyết lỗi. Tuy nhiên, tại sao .NET không giúp chỉ ra đối tượng nào có tham chiếu null và biểu thức nào đã kích hoạt NullReferenceException?

c#  .net 

2
Khi điều này xảy ra, hãy viết lại dòng nó đã xảy ra để nó kiểm tra từng kết quả có thể có để tìm giá trị rỗng trước - sau đó bạn sẽ biết chính xác nó là gì. Dù rằng, hoặc đã gỡ rối tuyệt vời của Visual Studio kèm theo, mà phá vỡ ngay lập tức một ngoại lệ xảy ra và cho phép bạn thấy là những gì vô :)
Patashu

5
Không thực sự, anh chỉ đơn giản là hỏi tại sao khuôn khổ NET không giúp các lập trình viên để hiển thị đối tượng là null. Tôi đoán đó là hình phạt về hiệu suất (bạn cần suy ngẫm). nhưng tôi cũng không chắc nữa.
bas

1
@bas: Mặc dù điều đó là đúng, nhưng câu hỏi hơi gây hiểu lầm ở chỗ nó nên hỏi về "một phần của biểu thức", không phải "đối tượng". Điều đó cũng giải thích tại sao phản ánh đơn thuần sẽ không hữu ích, nhưng một số thông tin gỡ lỗi mở rộng sẽ được yêu cầu.
HOẶC Người lập bản đồ

4
Tôi vẫn tò mò cho câu trả lời. Đó là loại tắt tương tự như trường hợp ngoại lệ .net không giúp chỉ ra đó chính không tồn tại trong từ điển. Ngoài ra, tôi không hiểu những cống hiến cho câu hỏi.
bas

12
Xin vui lòng thuật ngữ: Một đối tượng không bao giờ là rỗng. Một tham chiếu đối tượng có thể là mặc dù. Nhưng một tham chiếu đối tượng chỉ là một vị trí trong bộ nhớ - nó sẽ giúp ích gì cho bạn, trừ khi bạn có một trình gỡ lỗi được đính kèm?
Oskar Berggren

Câu trả lời:


169

(Để biết thông tin về trình trợ giúp ngoại lệ mới trong Visual Studio 2017, hãy xem phần cuối của câu trả lời này)


Hãy xem xét mã này:

String s = null;
Console.WriteLine(s.Length);

Điều này sẽ ném một NullReferenceExceptionvào dòng thứ hai và bạn muốn biết tại sao .NET không cho bạn biết rằng nó slà null khi ngoại lệ được ném.

Để hiểu tại sao bạn không nhận được thông tin đó, bạn nên nhớ rằng nó không phải là nguồn C # thực thi mà là IL:

IL_0001: ldnull      
IL_0002: stloc.0 // s
IL_0003: ldloc.0 // s
IL_0004: callvirt System.String.get_Length
IL_0009: gọi System.Console.WriteLine

Nó là callvirtopcode ném NullReferenceExceptionvà nó thực hiện điều đó khi đối số đầu tiên trên ngăn xếp đánh giá là một tham chiếu rỗng (tham chiếu đã được tải bằng cách sử dụng ldloc.0).

Nếu .NET có thể nói rằng đó là stham chiếu rỗng, theo một cách nào đó, nó sẽ theo dõi đối số đầu tiên trên biểu mẫu bắt nguồn của ngăn xếp đánh giá s. Trong trường hợp này, chúng ta dễ dàng thấy rằng đó là snull nhưng nếu giá trị đó là giá trị trả về từ một lệnh gọi hàm khác và không được lưu trữ trong bất kỳ biến nào? Dù sao, loại thông tin này không phải là thứ bạn muốn theo dõi trong một máy ảo như máy ảo .NET.


Để tránh vấn đề này, tôi khuyên bạn nên thực hiện kiểm tra đối số null trong tất cả các lệnh gọi phương thức công khai (tất nhiên trừ khi bạn cho phép tham chiếu null):

public void Foo(String s) {
  if (s == null)
    throw new ArgumentNullException("s");
  Console.WriteLine(s.Length);
}

Nếu null được truyền cho phương thức, bạn sẽ nhận được một ngoại lệ mô tả chính xác vấn đề là gì (đó slà null).


Bốn năm sau, Visual Studio 2017 hiện có một trình trợ giúp ngoại lệ mới sẽ cố gắng cho biết những gì là null khi một NullReferenceExceptionđược ném. Nó thậm chí có thể cung cấp cho bạn thông tin cần thiết khi nó là giá trị trả về của một phương thức là null:

Trình trợ giúp ngoại lệ Visual Studio 2017

Lưu ý rằng điều này chỉ hoạt động trong bản dựng GỠ LỖI.


5
Số dòng và tên tệp nguồn không được lưu trữ trong chính mã IL hay là chúng? Tuy nhiên, chúng có thể được tạo sẵn để gỡ lỗi.
HOẶC Mapper

4
@MartinLiversage: Chính xác. Vì vậy, câu hỏi đặt ra là: Tại sao không có đủ thông tin được lưu trữ trong các tệp ký hiệu cũng cho biết biểu thức trong mã được đánh giá là gì null.
HOẶC Người vẽ bản đồ

2
@MartinLiversage: Các tệp biểu tượng có thể truy cập theo cách để khi có ngoại lệ, cùng với thông báo ngoại lệ, tệp nguồn và số dòng có thể được hiển thị trong đầu ra của trình gỡ lỗi. Vì vậy, câu hỏi đặt ra là, lý do gì để không đưa thêm một số thông tin về những gì được trả về chính xác null- lưu ý rằng OP không tuyên bố rằng họ muốn biết rằng đối với các bản dựng phát hành, bản dựng gỡ lỗi có thể là đủ.
HOẶC Người vẽ bản đồ

3
Hmm, và chúng ta không nên quên rằng nó thậm chí không phải IL thực sự thực thi, mà là mã gốc được xây dựng từ nó trong thời gian chạy.
Oskar Berggren

2
@MartinLiversage: Không ai trong câu hỏi này tuyên bố rằng chúng tôi muốn điều này được hỗ trợ hoàn hảo cho các bản phát hành. Dù sao, tôi không hoàn toàn thấy vấn đề trong việc tương quan opcode IL sử dụng tham chiếu đối tượng (có thể là như vậy null) với dòng và cột tệp nguồn trả về tham chiếu đối tượng đó.
HOẶC Người vẽ bản đồ

9

Bạn muốn thông báo lỗi trong trường hợp sau trông như thế nào?

AnyObject.GetANullObject().ToString();

private object GetANullObject()
{
  return null;
}

Không có tên biến để báo cáo ở đây!


2
Tôi nghi ngờ OP đang tìm kiếm biểu thức trong mã nguồn trả về null, không phải đối tượng. Tôi đã thêm một bình luận tương ứng vào câu hỏi và hy vọng anh ấy hoặc cô ấy sẽ làm sáng tỏ mọi thứ. Nếu nghi ngờ của tôi là đúng, OP sẽ mong đợi một cái gì đó giống Object reference obtained from AnyObject.GetANullObject() not set to an instance of an object.như thông báo lỗi.
HOẶC Người vẽ bản đồ

1
@ORMapper Tôi đồng ý. Tôi sẽ chỉ đưa "câu trả lời" của tôi vào một bình luận cho OP, nếu tôi có đủ điểm danh tiếng để thêm một bình luận!
romar

1
"tham chiếu null đã cố gắng gọi phương thức ToString () của Class XYZ" sẽ hữu ích hơn những gì chúng ta nhận được bây giờ.
Michael Levy

Điều hữu ích nhất sẽ là một dấu vết ngăn xếp hiển thị, ở mỗi cấp độ cuộc gọi, chính xác dòng nào, trong tệp nào, đã dẫn đến lỗi. Ồ, chờ đã ... đó là những gì nó làm bây giờ!
Jim Balter

1

Vâng, đó là câu trả lời của các kỹ sư tại Microsoft. Nhưng rõ ràng bạn có thể sử dụng trình gỡ lỗi và thêm đồng hồ để tìm ra cái nào gặp sự cố.

Tuy nhiên, ngoại lệ NullReferenceExceptioncó nghĩa là tham chiếu không tồn tại . Bạn không thể lấy đối tượng chưa được tạo.

but why .NET don't tell us which object is null? Bởi vì nó không biết đối tượng nào là null. Đối tượng chỉ đơn giản là không tồn tại!

Tương tự là trường hợp khi tôi nói, C # được biên dịch thành mã .NET IL. Mã .NET IL không biết tên hoặc biểu thức. Nó chỉ biết tài liệu tham khảo và vị trí của chúng. Ở đây cũng vậy, bạn không thể có được những gì không tồn tại. Biểu thức hoặc tên biến không tồn tại.

Triết lý: Bạn không thể làm món omlette nếu bạn không có trứng ngay từ đầu.


3
đó cũng không phải là câu trả lời :)
bas

Làm thế nào để bạn có được tham chiếu nếu nó không tồn tại? @Bas
Aniket Inge

4
"Chà, đó là câu trả lời của các kỹ sư tại Microsoft.". Vì vậy, hãy để họ làm sáng tỏ điều này thay vì nói rõ điều hiển nhiên
bas

@bas, ít nhất chúng ta có thể quyết định điều gì là hợp lý. Về mặt logic, đối tượng không tồn tại. Làm thế nào bạn sẽ bắt nó với một ngoại lệ và sau đó in ra tên của đối tượng? Nó chỉ không tồn tại. Thậm chí không có trên ngăn xếp ..
Aniket Inge

vì vậy câu trả lời là hầu như không thể chỉ ra đối tượng nào có nullreference? Đó cũng là một câu trả lời. Tôi không nói rằng tôi biết, tôi chỉ thích câu hỏi :). +1 cho tất cả những nỗ lực: p
bas

1

Không chắc chắn, nhưng điều này có thể là do .Net không biết liệu nó là lớp được xác định trước hay do người dùng xác định. Nếu nó được xác định trước thì nó có thể là null (như chuỗi chiếm 2 Byte) nhưng nếu nó do người dùng định nghĩa thì chúng ta phải tạo một thể hiện của nó để nó biết rằng đối tượng này sẽ chiếm nhiều bộ nhớ này. Vì vậy, nó ném lỗi vào thời gian chạy.


-2

Câu hỏi hay. Hộp tin nhắn chỉ ngắn gọn là vô dụng. Ngay cả khi nó được chôn sâu một dặm so với định nghĩa tham chiếu, một số lớp hoặc tập hợp hoặc tệp hoặc thông tin khác sẽ tốt hơn những gì chúng hiện đang cung cấp (đọc: tốt hơn là không có gì).

Lựa chọn tốt nhất của bạn là chạy nó trong trình gỡ lỗi với thông tin gỡ lỗi và IDE của bạn sẽ ngắt ở dòng vi phạm (chứng minh rõ ràng hơn rằng thông tin hữu ích trên thực tế là có sẵn).

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.