NullReferenceException là gì và làm cách nào để khắc phục nó?


1875

Tôi có một số mã và khi nó thực thi, nó ném một NullReferenceException, nói:

Tham chiếu đối tượng không được đặt thành một thể hiện của một đối tượng.

Điều này có nghĩa là gì và tôi có thể làm gì để khắc phục lỗi này?


Trình trợ giúp ngoại lệ trong VS 2017 sẽ hữu ích hơn trong việc chẩn đoán nguyên nhân của ngoại lệ này - blog.msdn.microsoft.com/visualstudio/2016/11/11/ trên dưới Trình trợ giúp ngoại lệ mới .
Zev Spitz

Kính gửi khách truy cập trong tương lai, câu trả lời cho câu hỏi này cũng áp dụng tương tự cho một ArgumentNullException . Nếu câu hỏi của bạn đã bị đóng như là một bản sao của câu hỏi này và bạn đang gặp ANE, vui lòng làm theo hướng dẫn trong câu trả lời để gỡ lỗi và khắc phục vấn đề của bạn.

@will ANE chỉ nên xảy ra nếu null được truyền dưới dạng tham số. Bạn có thể đưa ra một ví dụ nếu một câu hỏi ANE đóng như một bản sao của câu hỏi này không?
John Saunders

Nó xuất hiện trên Meta, nhưng tôi phải đi đào liên kết. Nhưng theo nhận xét đó, ANE chỉ đơn giản là một NRE nhưng có ai đó đã thêm một kiểm tra phòng ngừa và ít nhất bạn biết chính xác null là gì (tên đối số được cung cấp), do đó, việc chẩn đoán dễ dàng hơn một chút so với NRE.

Câu trả lời:


2415

Nguyên nhân là gì?

Dòng dưới cùng

Bạn đang cố gắng sử dụng một cái gì đó null(hoặc Nothingtrong VB.NET). Điều này có nghĩa là bạn hoặc đặt nó thành null, hoặc bạn không bao giờ đặt nó thành bất cứ điều gì cả.

Giống như bất cứ điều gì khác, nullđược thông qua xung quanh. Nếu nó nằm null trong phương thức "A", thì đó có thể là phương thức "B" đã truyền a null cho phương thức "A".

null có thể có ý nghĩa khác nhau:

    1. Các biến đối tượng không được khởi tạo và do đó chỉ ra không có gì. Trong trường hợp này, nếu bạn truy cập các thuộc tính hoặc phương thức của các đối tượng đó, nó gây ra a NullReferenceException.
    1. Nhà phát triển đang sử dụng có nullchủ ý để chỉ ra rằng không có giá trị có ý nghĩa. Lưu ý rằng C # có khái niệm về kiểu dữ liệu nullable cho các biến (như bảng cơ sở dữ liệu có thể có các trường nullable) - bạn có thể gán nullcho chúng để chỉ ra rằng không có giá trị nào được lưu trữ trong đó, ví dụ như int? a = null;dấu hỏi cho biết nó được phép lưu trữ null biến a. Bạn có thể kiểm tra xem có if (a.HasValue) {...}hoặc với if (a==null) {...}. Các biến không thể, như aví dụ này, cho phép truy cập giá trị a.Valuemột cách rõ ràng hoặc chỉ như bình thường thông qua a.
      Lưu ý rằng việc truy cập nó thông qua a.Valueném một InvalidOperationExceptionthay vì một NullReferenceExceptionnếu anull- bạn nên thực hiện kiểm tra trước, tức là nếu bạn có một biến không thể khác int b;thì bạn nên thực hiện các bài tập như if (a.HasValue) { b = a.Value; }hoặc ngắn hơn if (a != null) { b = a; }.

Phần còn lại của bài viết này đi sâu vào chi tiết hơn và cho thấy những sai lầm mà nhiều lập trình viên thường mắc phải có thể dẫn đến a NullReferenceException.

Cụ thể hơn

Việc runtimeném NullReferenceException luôn luôn có nghĩa tương tự: bạn đang cố gắng sử dụng một tham chiếu và tham chiếu không được khởi tạo (hoặc nó đã được khởi tạo một lần , nhưng không còn được khởi tạo nữa ).

Điều này có nghĩa là tham chiếu là nullvà bạn không thể truy cập các thành viên (như phương thức) thông qua nulltham chiếu. Trường hợp đơn giản nhất:

string foo = null;
foo.ToUpper();

Điều này sẽ ném một NullReferenceExceptiondòng thứ hai vì bạn không thể gọi phương thức cá thể ToUpper()trên một stringtham chiếu trỏ đến null.

Gỡ lỗi

Làm thế nào để bạn tìm thấy nguồn của một NullReferenceException? Ngoài việc nhìn vào ngoại lệ, nó sẽ được ném chính xác tại vị trí xảy ra, các quy tắc gỡ lỗi chung trong Visual Studio áp dụng: đặt các điểm dừng chiến lược và kiểm tra các biến của bạn , bằng cách di chuột qua tên của chúng, mở một ( Nhanh) Xem cửa sổ hoặc sử dụng các bảng gỡ lỗi khác nhau như Địa phương và Ô tô.

Nếu bạn muốn tìm nơi tham chiếu được đặt hoặc không được đặt, nhấp chuột phải vào tên của nó và chọn "Tìm tất cả tài liệu tham khảo". Sau đó, bạn có thể đặt một điểm dừng ở mọi vị trí tìm thấy và chạy chương trình của bạn với trình gỡ lỗi được đính kèm. Mỗi khi trình gỡ lỗi phá vỡ điểm dừng như vậy, bạn cần xác định xem bạn có mong muốn tham chiếu là không null hay không, kiểm tra biến và xác minh rằng nó trỏ đến một thể hiện khi bạn mong đợi.

Bằng cách theo dõi chương trình theo cách này, bạn có thể tìm thấy vị trí mà cá thể không nên rỗng và tại sao nó không được đặt đúng.

Ví dụ

Một số tình huống phổ biến trong đó ngoại lệ có thể được ném:

Chung

ref1.ref2.ref3.member

Nếu ref1 hoặc ref2 hoặc ref3 là null, thì bạn sẽ nhận được a NullReferenceException. Nếu bạn muốn giải quyết vấn đề, sau đó tìm ra cái nào là null bằng cách viết lại biểu thức thành tương đương đơn giản hơn:

var r1 = ref1;
var r2 = r1.ref2;
var r3 = r2.ref3;
r3.member

Cụ thể, trong HttpContext.Current.User.Identity.Name, HttpContext.Currentcó thể là null hoặc Usertài sản có thể là null hoặc Identitytài sản có thể là null.

gián tiếp

public class Person 
{
    public int Age { get; set; }
}
public class Book 
{
    public Person Author { get; set; }
}
public class Example 
{
    public void Foo() 
    {
        Book b1 = new Book();
        int authorAge = b1.Author.Age; // You never initialized the Author property.
                                       // there is no Person to get an Age from.
    }
}

Nếu bạn muốn tránh tham chiếu null (Person) null, bạn có thể khởi tạo nó trong hàm tạo của đối tượng cha (Book).

Công cụ khởi tạo đối tượng lồng nhau

Điều tương tự cũng áp dụng cho các trình khởi tạo đối tượng lồng nhau:

Book b1 = new Book 
{ 
   Author = { Age = 45 } 
};

Điều này dịch sang

Book b1 = new Book();
b1.Author.Age = 45;

Trong khi newtừ khóa được sử dụng, nó chỉ tạo ra một thể hiện mới Book, nhưng không phải là một thể hiện mới Person, vì vậy Authorthuộc tính vẫn còn null.

Bộ khởi tạo bộ sưu tập lồng nhau

public class Person 
{
    public ICollection<Book> Books { get; set; }
}
public class Book 
{
    public string Title { get; set; }
}

Bộ sưu tập lồng nhau Initializershoạt động giống nhau:

Person p1 = new Person 
{
    Books = {
         new Book { Title = "Title1" },
         new Book { Title = "Title2" },
    }
};

Điều này dịch sang

Person p1 = new Person();
p1.Books.Add(new Book { Title = "Title1" });
p1.Books.Add(new Book { Title = "Title2" });

Các new Personchỉ tạo ra một thể hiện của Person, nhưng Booksbộ sưu tập vẫn là null. InitializerCú pháp bộ sưu tập không tạo ra một bộ sưu tập p1.Books, nó chỉ dịch sang các p1.Books.Add(...)câu lệnh.

Mảng

int[] numbers = null;
int n = numbers[0]; // numbers is null. There is no array to index.

Các phần tử mảng

Person[] people = new Person[5];
people[0].Age = 20 // people[0] is null. The array was allocated but not
                   // initialized. There is no Person to set the Age for.

Mảng răng cưa

long[][] array = new long[1][];
array[0][0] = 3; // is null because only the first dimension is yet initialized.
                 // Use array[0] = new long[2]; first.

Bộ sưu tập / Danh sách / Từ điển

Dictionary<string, int> agesForNames = null;
int age = agesForNames["Bob"]; // agesForNames is null.
                               // There is no Dictionary to perform the lookup.

Biến phạm vi (gián tiếp / hoãn lại)

public class Person 
{
    public string Name { get; set; }
}
var people = new List<Person>();
people.Add(null);
var names = from p in people select p.Name;
string firstName = names.First(); // Exception is thrown here, but actually occurs
                                  // on the line above.  "p" is null because the
                                  // first element we added to the list is null.

Sự kiện

public class Demo
{
    public event EventHandler StateChanged;

    protected virtual void OnStateChanged(EventArgs e)
    {        
        StateChanged(this, e); // Exception is thrown here 
                               // if no event handlers have been attached
                               // to StateChanged event
    }
}

###Bad Naming Conventions:

If you named fields differently from locals, you might have realized that you never initialized the field. 

lớp công khai Form1 {khách hàng riêng của khách hàng;

private void Form1_Load(object sender, EventArgs e) 
{
    Customer customer = new Customer();
    customer.Name = "John";
}

private void Button_Click(object sender, EventArgs e)
{
    MessageBox.Show(customer.Name);
}

}

Điều này có thể được giải quyết bằng cách tuân theo quy ước đối với các trường tiền tố có dấu gạch dưới:

    private Customer _customer;

Vòng đời trang ASP.NET:

public partial class Issues_Edit : System.Web.UI.Page
{
    protected TestIssue myIssue;

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
             // Only called on first load, not when button clicked
             myIssue = new TestIssue(); 
        }
    }

    protected void SaveButton_Click(object sender, EventArgs e)
    {
        myIssue.Entry = "NullReferenceException here!";
    }
}

Giá trị phiên ASP.NET

// if the "FirstName" session value has not yet been set,
// then this line will throw a NullReferenceException
string firstName = Session["FirstName"].ToString();

Mô hình xem ASP.NET MVC trống

Nếu ngoại lệ xảy ra khi tham chiếu một thuộc tính của @Modelmột ASP.NET MVC View, bạn cần hiểu rằng Modelđược đặt trong phương thức hành động của bạn, khi bạn returnxem. Khi bạn trả về một mô hình trống (hoặc thuộc tính mô hình) từ bộ điều khiển của bạn, ngoại lệ xảy ra khi các khung nhìn truy cập vào nó:

// Controller
public class Restaurant:Controller
{
    public ActionResult Search()
    {
        return View();  // Forgot the provide a Model here.
    }
}

// Razor view 
@foreach (var restaurantSearch in Model.RestaurantSearch)  // Throws.
{
}

<p>@Model.somePropertyName</p> <!-- Also throws -->

Sự kiện và thứ tự tạo điều khiển WPF

WPFđiều khiển được tạo trong khi gọi InitializeComponenttheo thứ tự chúng xuất hiện trong cây trực quan. A NullReferenceExceptionsẽ được nêu ra trong trường hợp các điều khiển được tạo sớm với các trình xử lý sự kiện, v.v., sẽ kích hoạt trong InitializeComponentđó tham chiếu các điều khiển được tạo muộn.

Ví dụ :

<Grid>
    <!-- Combobox declared first -->
    <ComboBox Name="comboBox1" 
              Margin="10"
              SelectedIndex="0" 
              SelectionChanged="comboBox1_SelectionChanged">
       <ComboBoxItem Content="Item 1" />
       <ComboBoxItem Content="Item 2" />
       <ComboBoxItem Content="Item 3" />
    </ComboBox>

    <!-- Label declared later -->
    <Label Name="label1" 
           Content="Label"
           Margin="10" />
</Grid>

Ở đây comboBox1được tạo ra trước label1. Nếu comboBox1_SelectionChangedcố gắng tham chiếu `nhãn1, nó sẽ chưa được tạo.

private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    label1.Content = comboBox1.SelectedIndex.ToString(); // NullReference here!!
}

Thay đổi thứ tự của các khai báo trong XAML(nghĩa là liệt kê label1trước comboBox1, bỏ qua các vấn đề về triết lý thiết kế, ít nhất sẽ giải quyết vấn đề NullReferenceExceptionở đây.

Diễn viên với as

var myThing = someObject as Thing;

Điều này không ném InvalidCastExceptionnhưng trả về một nullkhi diễn viên thất bại (và khi someObjectchính nó là null). Vì vậy, hãy nhận thức về điều đó.

LINQ FirstOrDefault()SingleOrDefault()

Các phiên bản đơn giản First()Single()ném ngoại lệ khi không có gì. Các phiên bản "OrDefault" trả về null trong trường hợp đó. Vì vậy, hãy nhận thức về điều đó.

cho mỗi

foreachném khi bạn cố gắng lặp bộ sưu tập null. Thường gây ra bởi nullkết quả bất ngờ từ các phương thức trả về bộ sưu tập.

List<int> list = null;    
foreach(var v in list) { } // exception

Ví dụ thực tế hơn - chọn các nút từ tài liệu XML. Sẽ ném nếu các nút không được tìm thấy nhưng gỡ lỗi ban đầu cho thấy tất cả các thuộc tính hợp lệ:

foreach (var node in myData.MyXml.DocumentNode.SelectNodes("//Data"))

Những cách để tránh

Hoàn toàn kiểm tra nullvà bỏ qua các giá trị null.

Nếu bạn cho rằng tham chiếu đôi khi là null, bạn có thể kiểm tra tham chiếu đó nulltrước khi truy cập các thành viên thể hiện:

void PrintName(Person p)
{
    if (p != null) 
    {
        Console.WriteLine(p.Name);
    }
}

Hoàn toàn kiểm tra nullvà cung cấp một giá trị mặc định.

Các phương thức gọi mà bạn mong đợi trả về một thể hiện có thể trả về null, ví dụ khi không thể tìm thấy đối tượng đang tìm kiếm. Bạn có thể chọn trả về giá trị mặc định khi gặp trường hợp này:

string GetCategory(Book b) 
{
    if (b == null)
        return "Unknown";
    return b.Category;
}

Hoàn toàn kiểm tra nulltừ các cuộc gọi phương thức và ném một ngoại lệ tùy chỉnh.

Bạn cũng có thể ném một ngoại lệ tùy chỉnh, chỉ để bắt nó trong mã cuộc gọi:

string GetCategory(string bookTitle) 
{
    var book = library.FindBook(bookTitle);  // This may return null
    if (book == null)
        throw new BookNotFoundException(bookTitle);  // Your custom exception
    return book.Category;
}

Sử dụng Debug.Assertnếu một giá trị không bao giờ nên null, để nắm bắt vấn đề sớm hơn ngoại lệ xảy ra.

Khi bạn biết trong quá trình phát triển, một phương thức có thể có thể, nhưng không bao giờ nên quay lại null, bạn có thể sử dụng Debug.Assert()để phá vỡ càng sớm càng tốt khi nó xảy ra:

string GetTitle(int knownBookID) 
{
    // You know this should never return null.
    var book = library.GetBook(knownBookID);  

    // Exception will occur on the next line instead of at the end of this method.
    Debug.Assert(book != null, "Library didn't return a book for known book ID.");

    // Some other code

    return book.Title; // Will never throw NullReferenceException in Debug mode.
}

Mặc dù kiểm tra này sẽ không kết thúc trong bản phát hành của bạn , khiến nó bị ném NullReferenceExceptionlại khi book == nullở chế độ chạy trong chế độ phát hành.

Sử dụng GetValueOrDefault()cho nullablecác loại giá trị để cung cấp một giá trị mặc định khi chúng là null.

DateTime? appointment = null;
Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));
// Will display the default value provided (DateTime.Now), because appointment is null.

appointment = new DateTime(2022, 10, 20);
Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));
// Will display the appointment date, not the default

Sử dụng toán tử hợp nhất null: ??[C #] hoặc If()[VB].

Tốc ký để cung cấp một giá trị mặc định khi nullgặp phải:

IService CreateService(ILogger log, Int32? frobPowerLevel)
{
   var serviceImpl = new MyService(log ?? NullLog.Instance);

   // Note that the above "GetValueOrDefault()" can also be rewritten to use
   // the coalesce operator:
   serviceImpl.FrobPowerLevel = frobPowerLevel ?? 5;
}

Sử dụng toán tử điều kiện null: ?.hoặc ?[x]cho các mảng (có sẵn trong C # 6 và VB.NET 14):

Điều này đôi khi cũng được gọi là toán tử điều hướng an toàn hoặc toán tử Elvis (sau hình dạng của nó). Nếu biểu thức ở phía bên trái của toán tử là null, thì phía bên phải sẽ không được ước tính và thay vào đó null được trả về. Điều đó có nghĩa là những trường hợp như thế này:

var title = person.Title.ToUpper();

Nếu người đó không có tiêu đề, điều này sẽ đưa ra một ngoại lệ vì họ đang cố gắng gọi ToUppermột tài sản có giá trị null.

Trong C# 5và dưới đây, điều này có thể được bảo vệ với:

var title = person.Title == null ? null : person.Title.ToUpper();

Bây giờ biến tiêu đề sẽ là null thay vì ném một ngoại lệ. C # 6 giới thiệu một cú pháp ngắn hơn cho việc này:

var title = person.Title?.ToUpper();

Điều này sẽ dẫn đến biến tiêu đề đang tồn tại nullvà cuộc gọi đến ToUpperkhông được thực hiện nếu person.Titlenull.

Tất nhiên, bạn vẫn phải kiểm tra titlenull hoặc sử dụng toán tử điều kiện null cùng với toán tử hợp nhất null ( ??) để cung cấp giá trị mặc định:

// regular null check
int titleLength = 0;
if (title != null)
    titleLength = title.Length; // If title is null, this would throw NullReferenceException

// combining the `?` and the `??` operator
int titleLength = title?.Length ?? 0;

Tương tự như vậy, đối với các mảng bạn có thể sử dụng ?[i]như sau:

int[] myIntArray=null;
var i=5;
int? elem = myIntArray?[i];
if (!elem.HasValue) Console.WriteLine("No value");

Điều này sẽ làm như sau: Nếu myIntArraylà null, biểu thức trả về null và bạn có thể kiểm tra nó một cách an toàn. Nếu nó chứa một mảng, nó sẽ làm tương tự như: elem = myIntArray[i];và trả về i<sup>th</sup>phần tử.

Sử dụng bối cảnh null (có sẵn trong C # 8):

Được giới thiệu trong C# 8đó các kiểu tham chiếu null và null null thực hiện phân tích tĩnh trên các biến và cung cấp cảnh báo trình biên dịch nếu một giá trị có thể có khả năng null hoặc đã được đặt thành null. Các kiểu tham chiếu nullable cho phép các kiểu được phép rõ ràng là null.

Bối cảnh chú thích nullable và bối cảnh cảnh báo nullable có thể được đặt cho một dự án bằng cách sử dụng Nullablephần tử trong csprojtệp của bạn . Phần tử này cấu hình cách trình biên dịch diễn giải tính không hợp lệ của các loại và những cảnh báo nào được tạo. Cài đặt hợp lệ là:

  • enable: Bối cảnh chú thích nullable được kích hoạt. Bối cảnh cảnh báo nullable được kích hoạt. Các biến của một kiểu tham chiếu, ví dụ, chuỗi là không rỗng. Tất cả các cảnh báo vô hiệu được kích hoạt.
  • vô hiệu hóa: bối cảnh chú thích nullable bị vô hiệu hóa. Bối cảnh cảnh báo nullable bị vô hiệu hóa. Các biến có kiểu tham chiếu là không biết, giống như các phiên bản trước của C #. Tất cả các cảnh báo vô hiệu đều bị vô hiệu hóa.
  • safeonly: Bối cảnh chú thích nullable được bật. Bối cảnh cảnh báo nullable là an toàn. Các biến có kiểu tham chiếu là không thể thay đổi. Tất cả các cảnh báo vô hiệu hóa an toàn được kích hoạt.
  • cảnh báo: Bối cảnh chú thích nullable bị vô hiệu hóa. Bối cảnh cảnh báo nullable được kích hoạt. Các biến của một kiểu tham chiếu là không biết. Tất cả các cảnh báo vô hiệu được kích hoạt.
  • safeonlywarnings: Bối cảnh chú thích nullable bị vô hiệu hóa. Bối cảnh cảnh báo nullable là an toàn. Các biến của một kiểu tham chiếu là không biết. Tất cả các cảnh báo vô hiệu hóa an toàn được kích hoạt.

Một loại tham chiếu nullable được ghi chú bằng cách sử dụng cú pháp giống như các loại giá trị nullable: a ?được gắn vào loại biến.

Các kỹ thuật đặc biệt để gỡ lỗi và sửa lỗi null derefs trong iterators

C#hỗ trợ "khối lặp" (được gọi là "trình tạo" trong một số ngôn ngữ phổ biến khác). Các trường hợp ngoại lệ của Null có thể đặc biệt khó khăn để gỡ lỗi trong các khối lặp vì thực thi bị trì hoãn:

public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
{
    for (int i = 0; i < count; ++i)
    yield return f.MakeFrob();
}
...
FrobFactory factory = whatever;
IEnumerable<Frobs> frobs = GetFrobs();
...
foreach(Frob frob in frobs) { ... }

Nếu whateverkết quả trong nullđó MakeFrobsẽ ném. Bây giờ, bạn có thể nghĩ rằng điều đúng đắn cần làm là:

// DON'T DO THIS
public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
{
   if (f == null) 
      throw new ArgumentNullException("f", "factory must not be null");
   for (int i = 0; i < count; ++i)
      yield return f.MakeFrob();
}

Tại sao điều này là sai? Bởi vì khối lặp không thực sự chạy cho đến khi foreach! Cuộc gọi GetFrobsđơn giản trả về một đối tượng mà khi lặp sẽ chạy khối lặp.

Bằng cách viết một kiểm tra null như thế này, bạn ngăn chặn sự vô hiệu hóa null, nhưng bạn di chuyển ngoại lệ đối số null đến điểm lặp , không đến điểm của cuộc gọi và điều đó rất khó hiểu để gỡ lỗi .

Cách khắc phục chính xác là:

// DO THIS
public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
{
   // No yields in a public method that throws!
   if (f == null) 
       throw new ArgumentNullException("f", "factory must not be null");
   return GetFrobsForReal(f, count);
}
private IEnumerable<Frob> GetFrobsForReal(FrobFactory f, int count)
{
   // Yields in a private method
   Debug.Assert(f != null);
   for (int i = 0; i < count; ++i)
        yield return f.MakeFrob();
}

Đó là, tạo một phương thức trợ giúp riêng có logic khối iterator và một phương thức bề mặt công khai thực hiện kiểm tra null và trả về iterator. Bây giờ khi GetFrobsđược gọi, kiểm tra null xảy ra ngay lập tức và sau đó GetFrobsForRealthực thi khi chuỗi được lặp lại.

Nếu bạn kiểm tra nguồn tham chiếu cho LINQcác Đối tượng, bạn sẽ thấy kỹ thuật này được sử dụng xuyên suốt. Nó hơi khó viết hơn một chút, nhưng nó làm cho việc sửa lỗi vô hiệu hóa dễ dàng hơn nhiều. Tối ưu hóa mã của bạn để thuận tiện cho người gọi, không phải là sự thuận tiện của tác giả .

Một lưu ý về các cuộc hội thảo null trong mã không an toàn

C#có chế độ "không an toàn", đúng như tên gọi của nó, cực kỳ nguy hiểm vì các cơ chế an toàn thông thường cung cấp an toàn bộ nhớ và an toàn loại không được thi hành. Bạn không nên viết mã không an toàn trừ khi bạn hiểu rõ và sâu sắc về cách thức hoạt động của bộ nhớ .

Trong chế độ không an toàn, bạn cần lưu ý đến hai sự thật quan trọng:

  • hội thảo một con trỏ null tạo ra ngoại lệ giống như hội thảo tham chiếu null
  • hội thảo một con trỏ không null không hợp lệ có thể tạo ra ngoại lệ đó trong một số trường hợp

Để hiểu lý do tại sao, điều đó giúp hiểu cách .NET tạo ra các ngoại lệ vô hiệu hóa null ngay từ đầu. (Các chi tiết này áp dụng cho .NET chạy trên Windows; các hệ điều hành khác sử dụng các cơ chế tương tự.)

Bộ nhớ được ảo hóa trong Windows; mỗi tiến trình có một không gian bộ nhớ ảo gồm nhiều "trang" bộ nhớ được hệ điều hành theo dõi. Mỗi trang của bộ nhớ có các cờ được đặt trên đó để xác định cách sử dụng: đọc từ, ghi vào, thực thi, v.v. Các thấp nhất trang được đánh dấu là "tạo ra một lỗi nếu từng sử dụng trong bất kỳ cách nào".

Cả một con trỏ null và một tham chiếu null trong C#đều được biểu diễn bên trong dưới dạng số 0 và do đó, bất kỳ nỗ lực nào để đưa nó vào bộ nhớ tương ứng của nó đều khiến hệ điều hành gây ra lỗi. Thời gian chạy .NET sau đó phát hiện lỗi này và biến nó thành ngoại lệ vô hiệu hóa null.

Đó là lý do tại sao hội nghị cả con trỏ null và tham chiếu null tạo ra cùng một ngoại lệ.

Còn điểm thứ hai thì sao? Việc hủy bỏ bất kỳ con trỏ không hợp lệ nào rơi vào trang thấp nhất của bộ nhớ ảo đều gây ra lỗi hệ điều hành tương tự và do đó cũng có ngoại lệ tương tự.

Tại sao điều này có ý nghĩa? Chà, giả sử chúng ta có một cấu trúc chứa hai số nguyên và một con trỏ không được quản lý bằng null. Nếu chúng tôi cố gắng hủy đăng ký int thứ hai trong cấu trúc, thì CLRsẽ không cố gắng truy cập vào bộ lưu trữ ở vị trí 0; nó sẽ truy cập vào bộ lưu trữ tại vị trí bốn. Nhưng về mặt logic, đây là một sự vô hiệu hóa bởi vì chúng tôi đang đến địa chỉ đó thông qua null.

Nếu bạn đang làm việc với mã không an toàn và bạn nhận được ngoại lệ vô hiệu hóa null, chỉ cần lưu ý rằng con trỏ vi phạm không cần phải là null. Nó có thể là bất kỳ vị trí nào trong trang thấp nhất và ngoại lệ này sẽ được tạo ra.


55
Có thể đây là một nhận xét ngu ngốc nhưng sẽ không phải là cách đầu tiên và tốt nhất để tránh vấn đề này là khởi tạo đối tượng? Đối với tôi nếu lỗi này xảy ra thường là do tôi quên khởi tạo một cái gì đó như phần tử mảng. Tôi nghĩ rằng nó ít phổ biến hơn để định nghĩa đối tượng là null và sau đó tham chiếu nó. Có thể đưa ra cách giải quyết từng vấn đề liền kề với mô tả. Vẫn là một bài viết tốt.
JPK

30
Điều gì xảy ra nếu không có đối tượng, mà là giá trị trả về từ một phương thức hoặc thuộc tính?
John Saunders

6
Ví dụ về cuốn sách / tác giả hơi kỳ lạ .... Làm thế nào mà nó thậm chí còn được biên dịch? Làm thế nào để intellisense thậm chí làm việc? Điều này là gì tôi không tốt với máy tính ...

5
@ Sẽ: chỉnh sửa cuối cùng của tôi có giúp được không? Nếu không, xin vui lòng rõ ràng hơn về những gì bạn thấy là một vấn đề.
John Saunders

6
@JohnSaunders Ồ, không, xin lỗi, ý tôi là phiên bản khởi tạo đối tượng của nó. new Book { Author = { Age = 45 } };Làm thế nào để khởi tạo bên trong thậm chí ... Tôi không thể nghĩ đến một tình huống mà init bên trong sẽ hoạt động, nhưng nó sẽ biên dịch và intellisense hoạt động ... Trừ khi cho các cấu trúc?

311

Ngoại lệ NullReference - Visual Basic

Các NullReference Exceptioncho Visual Basic là không có khác nhau từ một trong C # . Rốt cuộc, cả hai đều báo cáo cùng một ngoại lệ được xác định trong .NET Framework mà cả hai đều sử dụng. Nguyên nhân duy nhất đối với Visual Basic là rất hiếm (có lẽ chỉ có một).

Câu trả lời này sẽ sử dụng các thuật ngữ, cú pháp và ngữ cảnh của Visual Basic. Các ví dụ được sử dụng đến từ một số lượng lớn các câu hỏi Stack Overflow trước đây. Điều này là để tối đa hóa sự liên quan bằng cách sử dụng các loại tình huống thường thấy trong các bài đăng. Một chút giải thích cũng được cung cấp cho những người có thể cần nó. Một ví dụ tương tự như của bạn rất có thể được liệt kê ở đây.

Ghi chú:

  1. Đây là dựa trên khái niệm: không có mã để bạn dán vào dự án của bạn. Nó nhằm mục đích giúp bạn hiểu nguyên nhân gây ra NullReferenceException(NRE), cách tìm, cách khắc phục và cách tránh. Một NRE có thể được gây ra theo nhiều cách vì vậy đây không chắc là cuộc gặp gỡ duy nhất của bạn.
  2. Các ví dụ (từ bài viết Stack Overflow) không phải lúc nào cũng hiển thị cách tốt nhất để làm điều gì đó ở vị trí đầu tiên.
  3. Thông thường, phương thuốc đơn giản nhất được sử dụng.

Ý nghĩa cơ bản

Thông báo "Đối tượng không được đặt thành phiên bản của Đối tượng" có nghĩa là bạn đang cố gắng sử dụng một đối tượng chưa được khởi tạo. Điều này làm sôi một trong những điều sau:

  • Mã của bạn đã khai báo một biến đối tượng, nhưng nó không khởi tạo nó (tạo một thể hiện hoặc ' khởi tạo ' nó)
  • Một cái gì đó mà mã của bạn giả định sẽ khởi tạo một đối tượng, đã không
  • Có thể, mã khác vô hiệu sớm một đối tượng vẫn đang được sử dụng

Tìm nguyên nhân

Vì vấn đề là một tham chiếu đối tượng Nothing, nên câu trả lời là kiểm tra chúng để tìm ra cái nào. Sau đó xác định lý do tại sao nó không được khởi tạo. Giữ chuột trên các biến khác nhau và Visual Studio (VS) sẽ hiển thị giá trị của chúng - thủ phạm sẽ là Nothing.

Hiển thị gỡ lỗi IDE

Bạn cũng nên xóa bất kỳ khối Thử / Bắt nào khỏi mã có liên quan, đặc biệt là các khối không có gì trong khối Bắt. Điều này sẽ khiến mã của bạn bị sập khi nó cố sử dụng một đối tượng Nothing. Đây là những gì bạn muốn bởi vì nó sẽ xác định vị trí chính xác của vấn đề và cho phép bạn xác định đối tượng gây ra nó.

Một MsgBoxtrong Catch mà hiển thị Error while...sẽ giúp đỡ rất ít. Phương pháp này cũng dẫn đến các câu hỏi Stack Overflow rất tệ , bởi vì bạn không thể mô tả ngoại lệ thực tế, đối tượng liên quan hoặc thậm chí dòng mã nơi nó xảy ra.

Bạn cũng có thể sử dụng Locals Window( Gỡ lỗi -> Windows -> Địa phương ) để kiểm tra các đối tượng của bạn.

Một khi bạn biết vấn đề là gì và ở đâu, nó thường khá dễ sửa và nhanh hơn là đăng một câu hỏi mới.

Xem thêm:

Ví dụ và biện pháp khắc phục

Đối tượng lớp / Tạo một sơ thẩm

Dim reg As CashRegister
...
TextBox1.Text = reg.Amount         ' NRE

Vấn đề là Dimkhông tạo ra đối tượng CashRegister ; nó chỉ khai báo một biến có tên regLoại đó. Khai báo một biến đối tượng và tạo một thể hiện là hai điều khác nhau.

Biện pháp khắc phục

Các Newnhà điều hành thường có thể được sử dụng để tạo ra các ví dụ khi bạn khai báo nó:

Dim reg As New CashRegister        ' [New] creates instance, invokes the constructor

' Longer, more explicit form:
Dim reg As CashRegister = New CashRegister

Khi nó chỉ thích hợp để tạo cá thể sau:

Private reg As CashRegister         ' Declare
  ...
reg = New CashRegister()            ' Create instance

Lưu ý: Không sử dụng Dimlại trong một quy trình, bao gồm cả hàm tạo ( Sub New):

Private reg As CashRegister
'...

Public Sub New()
   '...
   Dim reg As New CashRegister
End Sub

Điều này sẽ tạo ra một địa phương biến, regmà chỉ tồn tại trong bối cảnh đó (phụ). Các regbiến với cấp module Scopemà bạn sẽ sử dụng cốt khắp mọi nơi khác Nothing.

Thiếu Newtoán tử là nguyên nhân số 1NullReference Exceptions được thấy trong các câu hỏi Stack Overflow được xem xét.

Visual Basic cố gắng làm cho quá trình rõ ràng lặp đi lặp lại bằng cách sử dụng New: Sử dụng NewToán tử tạo một đối tượng mới và gọi Sub New- hàm tạo - nơi đối tượng của bạn có thể thực hiện bất kỳ khởi tạo nào khác.

Để rõ ràng, Dim(hoặc Private) chỉ khai báo một biến và của nó Type. Các Phạm vi của biến - cho dù nó tồn tại cho module toàn bộ / lớp hoặc là địa phương để một thủ tục - được xác định bởi nơi nó được khai báo. Private | Friend | Publicxác định cấp độ truy cập, không phải Phạm vi .

Để biết thêm thông tin, xem:


Mảng

Mảng cũng phải được khởi tạo:

Private arr as String()

Mảng này chỉ được khai báo, không được tạo. Có một số cách để khởi tạo một mảng:

Private arr as String() = New String(10){}
' or
Private arr() As String = New String(10){}

' For a local array (in a procedure) and using 'Option Infer':
Dim arr = New String(10) {}

Lưu ý: Bắt đầu với VS 2010, khi khởi tạo một mảng cục bộ bằng cách sử dụng một chữ và Option Infer, các phần tử As <Type>Newlà tùy chọn:

Dim myDbl As Double() = {1.5, 2, 9.9, 18, 3.14}
Dim myDbl = New Double() {1.5, 2, 9.9, 18, 3.14}
Dim myDbl() = {1.5, 2, 9.9, 18, 3.14}

Kiểu dữ liệu và kích thước mảng được suy ra từ dữ liệu được gán. Khai báo mức độ lớp / mô-đun vẫn yêu cầu As <Type>với Option Strict:

Private myDoubles As Double() = {1.5, 2, 9.9, 18, 3.14}

Ví dụ: Mảng các đối tượng lớp

Dim arrFoo(5) As Foo

For i As Integer = 0 To arrFoo.Count - 1
   arrFoo(i).Bar = i * 10       ' Exception
Next

Mảng đã được tạo, nhưng các Foođối tượng trong đó thì không.

Biện pháp khắc phục

For i As Integer = 0 To arrFoo.Count - 1
    arrFoo(i) = New Foo()         ' Create Foo instance
    arrFoo(i).Bar = i * 10
Next

Việc sử dụng List(Of T)sẽ làm cho việc có một phần tử không có đối tượng hợp lệ là khá khó khăn:

Dim FooList As New List(Of Foo)     ' List created, but it is empty
Dim f As Foo                        ' Temporary variable for the loop

For i As Integer = 0 To 5
    f = New Foo()                    ' Foo instance created
    f.Bar =  i * 10
    FooList.Add(f)                   ' Foo object added to list
Next

Để biết thêm thông tin, xem:


Danh sách và Bộ sưu tập

Các bộ sưu tập .NET (trong đó có nhiều loại - Danh sách, Từ điển, v.v.) cũng phải được khởi tạo hoặc tạo.

Private myList As List(Of String)
..
myList.Add("ziggy")           ' NullReference

Bạn nhận được cùng một ngoại lệ cho cùng một lý do - myListchỉ được khai báo, nhưng không có trường hợp nào được tạo. Biện pháp khắc phục là như nhau:

myList = New List(Of String)

' Or create an instance when declared:
Private myList As New List(Of String)

Một giám sát chung là một lớp sử dụng một bộ sưu tập Type:

Public Class Foo
    Private barList As List(Of Bar)

    Friend Function BarCount As Integer
        Return barList.Count
    End Function

    Friend Sub AddItem(newBar As Bar)
        If barList.Contains(newBar) = False Then
            barList.Add(newBar)
        End If
    End Function

Cả hai thủ tục sẽ dẫn đến một NRE, bởi vì barListchỉ được khai báo, không được khởi tạo. Tạo một thể hiện của Foocũng sẽ không tạo ra một thể hiện của nội bộ barList. Nó có thể có ý định làm điều này trong hàm tạo:

Public Sub New         ' Constructor
    ' Stuff to do when a new Foo is created...
    barList = New List(Of Bar)
End Sub

Như trước đây, điều này không chính xác:

Public Sub New()
    ' Creates another barList local to this procedure
     Dim barList As New List(Of Bar)
End Sub

Để biết thêm thông tin, xem List(Of T)Lớp .


Đối tượng cung cấp dữ liệu

Làm việc với cơ sở dữ liệu trình bày nhiều cơ hội cho một NullReference bởi vì có thể có nhiều đối tượng ( Command, Connection, Transaction, Dataset, DataTable, DataRows....) sử dụng cùng một lúc. Lưu ý: Không quan trọng bạn đang sử dụng nhà cung cấp dữ liệu nào - MySQL, SQL Server, OleDB, v.v. - các khái niệm đều giống nhau.

ví dụ 1

Dim da As OleDbDataAdapter
Dim ds As DataSet
Dim MaxRows As Integer

con.Open()
Dim sql = "SELECT * FROM tblfoobar_List"
da = New OleDbDataAdapter(sql, con)
da.Fill(ds, "foobar")
con.Close()

MaxRows = ds.Tables("foobar").Rows.Count      ' Error

Như trước đây, dsđối tượng Dataset đã được khai báo, nhưng một thể hiện không bao giờ được tạo. Các DataAdaptersẽ điền vào một hiện DataSet, không tạo ra một. Trong trường hợp này, do dslà một biến cục bộ, IDE cảnh báo bạn rằng điều này có thể xảy ra:

img

Khi được khai báo là biến cấp mô-đun / lớp, như có vẻ là trường hợp với con, trình biên dịch không thể biết liệu đối tượng có được tạo bởi một thủ tục ngược dòng hay không. Đừng bỏ qua các cảnh báo.

Biện pháp khắc phục

Dim ds As New DataSet

Ví dụ 2

ds = New DataSet
da = New OleDBDataAdapter(sql, con)
da.Fill(ds, "Employees")

txtID.Text = ds.Tables("Employee").Rows(0).Item(1)
txtID.Name = ds.Tables("Employee").Rows(0).Item(2)

Một typo là một vấn đề ở đây: Employeesvs Employee. Không có DataTabletên "Nhân viên" được tạo ra, vì vậy NullReferenceExceptionkết quả cố gắng truy cập nó. Một vấn đề tiềm năng khác là giả sử sẽ có Itemsđiều đó có thể không xảy ra khi SQL bao gồm mệnh đề WHERE.

Biện pháp khắc phục

Vì điều này sử dụng một bảng, sử dụng Tables(0)sẽ tránh lỗi chính tả. Kiểm tra Rows.Countcũng có thể giúp:

If ds.Tables(0).Rows.Count > 0 Then
    txtID.Text = ds.Tables(0).Rows(0).Item(1)
    txtID.Name = ds.Tables(0).Rows(0).Item(2)
End If

Filllà một hàm trả về số lượng Rowsảnh hưởng cũng có thể được kiểm tra:

If da.Fill(ds, "Employees") > 0 Then...

Ví dụ 3

Dim da As New OleDb.OleDbDataAdapter("SELECT TICKET.TICKET_NO,
        TICKET.CUSTOMER_ID, ... FROM TICKET_RESERVATION AS TICKET INNER JOIN
        FLIGHT_DETAILS AS FLIGHT ... WHERE [TICKET.TICKET_NO]= ...", con)
Dim ds As New DataSet
da.Fill(ds)

If ds.Tables("TICKET_RESERVATION").Rows.Count > 0 Then

Các DataAdaptersẽ cung cấp TableNamesnhư trong ví dụ trước, nhưng nó không tên phân tích cú pháp từ SQL hoặc cơ sở dữ liệu bảng. Kết quả là, ds.Tables("TICKET_RESERVATION")tham chiếu một bảng không tồn tại.

Các Biện pháp khắc phục là như nhau, tham khảo bảng bằng chỉ số:

If ds.Tables(0).Rows.Count > 0 Then

Xem thêm Lớp DataTable .


Đường dẫn đối tượng / lồng nhau

If myFoo.Bar.Items IsNot Nothing Then
   ...

Mã chỉ đang thử nghiệm Itemstrong khi cả hai myFooBarcũng có thể là Không có gì. Biện pháp khắc phục là kiểm tra toàn bộ chuỗi hoặc đường đi của các đối tượng cùng một lúc:

If (myFoo IsNot Nothing) AndAlso
    (myFoo.Bar IsNot Nothing) AndAlso
    (myFoo.Bar.Items IsNot Nothing) Then
    ....

AndAlsolà quan trọng Các xét nghiệm sau đó sẽ không được thực hiện khi Falsegặp điều kiện đầu tiên . Điều này cho phép mã 'khoan' một cách an toàn vào (các) đối tượng một cấp 'tại một thời điểm, myFoo.Barchỉ đánh giá sau khi (và nếu) myFoođược xác định là hợp lệ. Chuỗi đối tượng hoặc đường dẫn có thể khá dài khi mã hóa các đối tượng phức tạp:

myBase.myNodes(3).Layer.SubLayer.Foo.Files.Add("somefilename")

Không thể tham chiếu bất cứ điều gì 'xuôi dòng' của một nullđối tượng. Điều này cũng áp dụng cho các điều khiển:

myWebBrowser.Document.GetElementById("formfld1").InnerText = "some value"

Ở đây, myWebBrowserhoặc Documentcó thể là Không có gì hoặc formfld1phần tử có thể không tồn tại.


Kiểm soát giao diện người dùng

Dim cmd5 As New SqlCommand("select Cartons, Pieces, Foobar " _
     & "FROM Invoice where invoice_no = '" & _
     Me.ComboBox5.SelectedItem.ToString.Trim & "' And category = '" & _
     Me.ListBox1.SelectedItem.ToString.Trim & "' And item_name = '" & _
     Me.ComboBox2.SelectedValue.ToString.Trim & "' And expiry_date = '" & _
     Me.expiry.Text & "'", con)

Trong số những thứ khác, mã này không lường trước được rằng người dùng có thể không chọn thứ gì đó trong một hoặc nhiều điều khiển UI. ListBox1.SelectedItemcũng có thể Nothing, do đó ListBox1.SelectedItem.ToStringsẽ dẫn đến một NRE.

Biện pháp khắc phục

Xác thực dữ liệu trước khi sử dụng (cũng sử dụng Option Strictvà tham số SQL):

Dim expiry As DateTime         ' for text date validation
If (ComboBox5.SelectedItems.Count > 0) AndAlso
    (ListBox1.SelectedItems.Count > 0) AndAlso
    (ComboBox2.SelectedItems.Count > 0) AndAlso
    (DateTime.TryParse(expiry.Text, expiry) Then

    '... do stuff
Else
    MessageBox.Show(...error message...)
End If

Ngoài ra, bạn có thể sử dụng (ComboBox5.SelectedItem IsNot Nothing) AndAlso...


Hình thức cơ bản trực quan

Public Class Form1

    Private NameBoxes = New TextBox(5) {Controls("TextBox1"), _
                   Controls("TextBox2"), Controls("TextBox3"), _
                   Controls("TextBox4"), Controls("TextBox5"), _
                   Controls("TextBox6")}

    ' same thing in a different format:
    Private boxList As New List(Of TextBox) From {TextBox1, TextBox2, TextBox3 ...}

    ' Immediate NRE:
    Private somevar As String = Me.Controls("TextBox1").Text

Đây là một cách khá phổ biến để có được một NRE. Trong C #, tùy thuộc vào cách mã hóa, IDE sẽ báo cáo Controlskhông tồn tại trong ngữ cảnh hiện tại hoặc "không thể tham chiếu thành viên không tĩnh". Vì vậy, ở một mức độ nào đó, đây là một tình huống chỉ có VB. Nó cũng phức tạp vì nó có thể dẫn đến một thác thất bại.

Các mảng và bộ sưu tập không thể được khởi tạo theo cách này. Mã khởi tạo này sẽ chạy trước khi hàm tạo tạo Formhoặc Controls. Kết quả là:

  • Danh sách và Bộ sưu tập sẽ đơn giản là trống
  • Mảng sẽ chứa năm yếu tố của Không có gì
  • Việc somevarchuyển nhượng sẽ dẫn đến một NRE ngay lập tức vì Không có gì không có .Texttài sản

Tham chiếu các phần tử mảng sau này sẽ dẫn đến một NRE. Nếu bạn thực hiện việc này Form_Load, do một lỗi lạ, IDE có thể không báo cáo ngoại lệ khi nó xảy ra. Ngoại lệ sẽ bật lên sau khi mã của bạn cố gắng sử dụng mảng. "Ngoại lệ im lặng" này được trình bày chi tiết trong bài viết này . Đối với mục đích của chúng tôi, điều quan trọng là khi một điều gì đó thảm khốc xảy ra trong khi tạo biểu mẫu ( Sub Newhoặc Form Loadsự kiện), các ngoại lệ có thể không được đưa ra, mã sẽ thoát khỏi quy trình và chỉ hiển thị biểu mẫu.

Vì không có mã nào khác trong sự kiện Sub Newhoặc Form Loadsự kiện của bạn sẽ chạy sau NRE, rất nhiều thứ khác có thể bị bỏ lại.

Sub Form_Load(..._
   '...
   Dim name As String = NameBoxes(2).Text        ' NRE
   ' ...
   ' More code (which will likely not be executed)
   ' ...
End Sub

Lưu ý điều này áp dụng cho bất kỳ và tất cả các tham chiếu kiểm soát và thành phần làm cho chúng bất hợp pháp ở nơi chúng:

Public Class Form1

    Private myFiles() As String = Me.OpenFileDialog1.FileName & ...
    Private dbcon As String = OpenFileDialog1.FileName & ";Jet Oledb..."
    Private studentName As String = TextBox13.Text

Biện pháp khắc phục một phần

Đó là tò mò mà VB không cung cấp một cảnh báo, nhưng biện pháp khắc phục là để tuyên bố các container ở cấp hình thức, nhưng khởi tạo chúng trong xử lý sự kiện dưới hình thức tải khi các điều khiển làm tồn tại. Điều này có thể được thực hiện Sub Newmiễn là mã của bạn sau InitializeComponentcuộc gọi:

' Module level declaration
Private NameBoxes as TextBox()
Private studentName As String

' Form Load, Form Shown or Sub New:
'
' Using the OP's approach (illegal using OPTION STRICT)
NameBoxes = New TextBox() {Me.Controls("TextBox1"), Me.Controls("TestBox2"), ...)
studentName = TextBox32.Text           ' For simple control references

Mã mảng có thể chưa ra khỏi rừng. Bất kỳ điều khiển nào trong điều khiển vùng chứa (như một GroupBoxhoặc Panel) sẽ không được tìm thấy trong Me.Controls; chúng sẽ nằm trong bộ sưu tập Điều khiển của Bảng điều khiển hoặc GroupBox đó. Một điều khiển cũng sẽ không được trả về khi tên điều khiển bị sai chính tả ( "TeStBox2"). Trong các trường hợp như vậy, Nothingmột lần nữa sẽ được lưu trữ trong các phần tử mảng đó và NRE sẽ dẫn đến kết quả khi bạn cố gắng tham chiếu nó.

Chúng nên dễ dàng tìm thấy ngay bây giờ khi bạn biết những gì bạn đang tìm kiếm: VS chỉ cho bạn lỗi của bạn

"Nút2" nằm trên một Panel

Biện pháp khắc phục

Thay vì tham chiếu gián tiếp theo tên bằng cách sử dụng Controlsbộ sưu tập của biểu mẫu , hãy sử dụng tham chiếu điều khiển:

' Declaration
Private NameBoxes As TextBox()

' Initialization -  simple and easy to read, hard to botch:
NameBoxes = New TextBox() {TextBox1, TextBox2, ...)

' Initialize a List
NamesList = New List(Of TextBox)({TextBox1, TextBox2, TextBox3...})
' or
NamesList = New List(Of TextBox)
NamesList.AddRange({TextBox1, TextBox2, TextBox3...})

Chức năng trả lại không có gì

Private bars As New List(Of Bars)        ' Declared and created

Public Function BarList() As List(Of Bars)
    bars.Clear
    If someCondition Then
        For n As Integer = 0 to someValue
            bars.Add(GetBar(n))
        Next n
    Else
        Exit Function
    End If

    Return bars
End Function

Đây là trường hợp IDE sẽ cảnh báo bạn rằng ' không phải tất cả các đường dẫn đều trả về giá trị và NullReferenceExceptionkết quả có thể xảy ra '. Bạn có thể ngăn chặn cảnh báo, bằng cách thay thế Exit Functionbằng Return Nothing, nhưng điều đó không giải quyết được vấn đề. Bất cứ điều gì cố gắng sử dụng trả lại khi nào someCondition = Falsesẽ dẫn đến NRE:

bList = myFoo.BarList()
For Each b As Bar in bList      ' EXCEPTION
      ...

Biện pháp khắc phục

Thay thế Exit Functiontrong chức năng với Return bList. Trả lại một sản phẩm nào List không giống như trả lại Nothing. Nếu có thể có một đối tượng được trả về Nothing, hãy kiểm tra trước khi sử dụng nó:

 bList = myFoo.BarList()
 If bList IsNot Nothing Then...

Thử / bắt kém

Thử / Bắt được triển khai kém có thể che giấu vấn đề ở đâu và dẫn đến những vấn đề mới:

Dim dr As SqlDataReader
Try
    Dim lnk As LinkButton = TryCast(sender, LinkButton)
    Dim gr As GridViewRow = DirectCast(lnk.NamingContainer, GridViewRow)
    Dim eid As String = GridView1.DataKeys(gr.RowIndex).Value.ToString()
    ViewState("username") = eid
    sqlQry = "select FirstName, Surname, DepartmentName, ExtensionName, jobTitle,
             Pager, mailaddress, from employees1 where username='" & eid & "'"
    If connection.State <> ConnectionState.Open Then
        connection.Open()
    End If
    command = New SqlCommand(sqlQry, connection)

    'More code fooing and barring

    dr = command.ExecuteReader()
    If dr.Read() Then
        lblFirstName.Text = Convert.ToString(dr("FirstName"))
        ...
    End If
    mpe.Show()
Catch

Finally
    command.Dispose()
    dr.Close()             ' <-- NRE
    connection.Close()
End Try

Đây là một trường hợp của một đối tượng không được tạo ra như mong đợi, nhưng cũng cho thấy tính hữu dụng của bộ trống Catch.

Có một dấu phẩy thừa trong SQL (sau 'mailaddress') dẫn đến ngoại lệ tại .ExecuteReader. Sau khi Catchkhông làm gì, Finallycố gắng thực hiện dọn dẹp, nhưng vì bạn không thể Closelà một DataReaderđối tượng null , một NullReferenceExceptionkết quả hoàn toàn mới .

Một Catchkhối trống là sân chơi của quỷ. OP này đã gặp khó khăn tại sao anh ta nhận được một NRE trong Finallykhối. Trong các tình huống khác, một sản phẩm nào đó Catchcó thể dẫn đến một thứ gì đó khác ở phía dưới sẽ đi sâu hơn và khiến bạn mất thời gian để nhìn vào những thứ sai ở vị trí sai cho vấn đề. ("Ngoại lệ im lặng" được mô tả ở trên cung cấp cùng một giá trị giải trí.)

Biện pháp khắc phục

Không sử dụng các khối Thử / Bắt trống - hãy để mã bị sập để bạn có thể a) xác định nguyên nhân b) xác định vị trí và c) áp dụng một biện pháp khắc phục thích hợp. Các khối Thử / Bắt không nhằm che giấu ngoại lệ khỏi người đủ điều kiện duy nhất để sửa chúng - nhà phát triển.


DBNull không giống như Không có gì

For Each row As DataGridViewRow In dgvPlanning.Rows
    If Not IsDBNull(row.Cells(0).Value) Then
        ...

Các IsDBNullchức năng được sử dụng để kiểm tra nếu một giá trị tương đương với System.DBNull: Từ MSDN:

Giá trị System.DBNull chỉ ra rằng Đối tượng biểu thị dữ liệu bị thiếu hoặc không tồn tại. DBNull không giống như Không có gì, chỉ ra rằng một biến chưa được khởi tạo.

Biện pháp khắc phục

If row.Cells(0) IsNot Nothing Then ...

Như trước đây, bạn có thể kiểm tra Không có gì, sau đó cho một giá trị cụ thể:

If (row.Cells(0) IsNot Nothing) AndAlso (IsDBNull(row.Cells(0).Value) = False) Then

Ví dụ 2

Dim getFoo = (From f In dbContext.FooBars
               Where f.something = something
               Select f).FirstOrDefault

If Not IsDBNull(getFoo) Then
    If IsDBNull(getFoo.user_id) Then
        txtFirst.Text = getFoo.first_name
    Else
       ...

FirstOrDefaulttrả về mục đầu tiên hoặc giá trị mặc định, Nothingdành cho các loại tham chiếu và không bao giờ DBNull:

If getFoo IsNot Nothing Then...

Kiểm soát

Dim chk As CheckBox

chk = CType(Me.Controls(chkName), CheckBox)
If chk.Checked Then
    Return chk
End If

Nếu một CheckBoxvới chkNamekhông thể được tìm thấy (hoặc tồn tại trong một GroupBox), sau đó chksẽ có gì và được cố gắng để tham khảo bất kỳ bất động sản sẽ dẫn đến một ngoại lệ.

Biện pháp khắc phục

If (chk IsNot Nothing) AndAlso (chk.Checked) Then ...

DataGridView

DGV có một vài quirks được nhìn thấy định kỳ:

dgvBooks.DataSource = loan.Books
dgvBooks.Columns("ISBN").Visible = True       ' NullReferenceException
dgvBooks.Columns("Title").DefaultCellStyle.Format = "C"
dgvBooks.Columns("Author").DefaultCellStyle.Format = "C"
dgvBooks.Columns("Price").DefaultCellStyle.Format = "C"

Nếu dgvBooksAutoGenerateColumns = True, nó sẽ tạo các cột, nhưng nó không đặt tên cho chúng, vì vậy đoạn mã trên không thành công khi tham chiếu chúng theo tên.

Biện pháp khắc phục

Đặt tên cho các cột theo cách thủ công hoặc tham chiếu theo chỉ mục:

dgvBooks.Columns(0).Visible = True

Ví dụ 2 - Coi chừng NewRow

xlWorkSheet = xlWorkBook.Sheets("sheet1")

For i = 0 To myDGV.RowCount - 1
    For j = 0 To myDGV.ColumnCount - 1
        For k As Integer = 1 To myDGV.Columns.Count
            xlWorkSheet.Cells(1, k) = myDGV.Columns(k - 1).HeaderText
            xlWorkSheet.Cells(i + 2, j + 1) = myDGV(j, i).Value.ToString()
        Next
    Next
Next

Khi bạn DataGridViewAllowUserToAddRowsas True(mặc định), Cellshàng trống / mới ở phía dưới sẽ chứa tất cả Nothing. Hầu hết các nỗ lực sử dụng nội dung (ví dụ ToString:) sẽ dẫn đến NRE.

Biện pháp khắc phục

Sử dụng một For/Eachvòng lặp và kiểm tra thuộc IsNewRowtính để xác định nếu đó là hàng cuối cùng. Điều này hoạt động cho dù AllowUserToAddRowslà đúng hay không:

For Each r As DataGridViewRow in myDGV.Rows
    If r.IsNewRow = False Then
         ' ok to use this row

Nếu bạn sử dụng một For nvòng lặp, hãy sửa đổi số hàng hoặc sử dụng Exit Forkhi nào IsNewRowlà đúng.


My.Sinstall (StringCollection)

Trong một số trường hợp nhất định, việc cố gắng sử dụng một vật phẩm My.Settingsmà từ đó StringCollectioncó thể dẫn đến NullReference trong lần đầu tiên bạn sử dụng nó. Giải pháp là như nhau, nhưng không rõ ràng. Xem xét:

My.Settings.FooBars.Add("ziggy")         ' foobars is a string collection

Vì VB đang quản lý Cài đặt cho bạn, nên việc khởi tạo bộ sưu tập là điều hợp lý. Nó sẽ, nhưng chỉ khi trước đó bạn đã thêm một mục nhập ban đầu vào bộ sưu tập (trong trình chỉnh sửa Cài đặt). Vì bộ sưu tập (dường như) được khởi tạo khi một mục được thêm vào, nên nó vẫn còn Nothingkhi không có mục nào trong trình chỉnh sửa Cài đặt để thêm.

Biện pháp khắc phục

Khởi tạo bộ sưu tập cài đặt trong Loadtrình xử lý sự kiện của biểu mẫu , nếu / khi cần:

If My.Settings.FooBars Is Nothing Then
    My.Settings.FooBars = New System.Collections.Specialized.StringCollection
End If

Thông thường, Settingsbộ sưu tập sẽ chỉ cần được khởi tạo trong lần đầu tiên ứng dụng chạy. Một biện pháp khắc phục thay thế là thêm giá trị ban đầu vào bộ sưu tập của bạn trong Dự án -> Cài đặt | FooBars , lưu dự án, sau đó loại bỏ giá trị giả.


Những điểm chính

Bạn có thể quên các Newnhà điều hành.

hoặc là

Một cái gì đó bạn giả định sẽ thực hiện hoàn hảo để trả lại một đối tượng được khởi tạo cho mã của bạn, thì không.

Đừng bỏ qua các cảnh báo của trình biên dịch (bao giờ) và sử dụng Option Strict On(luôn luôn).


MSDN NullReference Ngoại lệ


226

Một kịch bản khác là khi bạn chuyển một đối tượng null thành một loại giá trị . Ví dụ: mã dưới đây:

object o = null;
DateTime d = (DateTime)o;

Nó sẽ ném một NullReferenceExceptiondiễn viên. Có vẻ khá rõ ràng trong mẫu ở trên, nhưng điều này có thể xảy ra trong các tình huống phức tạp "ràng buộc muộn" hơn trong đó đối tượng null đã được trả về từ một số mã bạn không sở hữu và ví dụ như đúc được tạo bởi một hệ thống tự động.

Một ví dụ về điều này là đoạn liên kết ASP.NET đơn giản này với điều khiển Lịch:

<asp:Calendar runat="server" SelectedDate="<%#Bind("Something")%>" />

SelectedDateTrên thực tế , đây là một thuộc tính - thuộc DateTimeloại - thuộc Calendarloại Điều khiển Web và ràng buộc hoàn toàn có thể trả về một cái gì đó không có giá trị. Trình tạo ASP.NET ẩn sẽ tạo ra một đoạn mã tương đương với mã cast ở trên. Và điều này sẽ đưa ra một NullReferenceExceptionđiều khá khó để phát hiện ra, bởi vì nó nằm trong mã được tạo bởi ASP.NET sẽ biên dịch tốt ...


7
Cú bắt tuyệt vời. Cách tránh một lần lót:DateTime x = (DateTime) o as DateTime? ?? defaultValue;
Serge Shultz

160

Nó có nghĩa là các biến trong câu hỏi được chỉ vào không có gì. Tôi có thể tạo ra điều này như vậy:

SqlConnection connection = null;
connection.Open();

Điều đó sẽ gây ra lỗi vì trong khi tôi đã khai báo biến " connection", nó không được chỉ ra bất cứ điều gì. Khi tôi cố gắng gọi thành viên " Open", không có tài liệu tham khảo nào để giải quyết và nó sẽ đưa ra lỗi.

Để tránh lỗi này:

  1. Luôn khởi tạo các đối tượng của bạn trước khi bạn cố gắng làm bất cứ điều gì với chúng.
  2. Nếu bạn không chắc chắn liệu đối tượng có null hay không, hãy kiểm tra nó với object == null.

Công cụ chia sẻ lại của JetBrains sẽ xác định mọi vị trí trong mã của bạn có khả năng xảy ra lỗi tham chiếu null, cho phép bạn đặt kiểm tra null. Lỗi này là nguồn lỗi số một, IMHO.


3
Công cụ chia sẻ lại của JetBrains sẽ xác định mọi vị trí trong mã của bạn có khả năng xảy ra lỗi tham chiếu null. Điều này là không chính xác. Tôi có một giải pháp mà không có sự phát hiện đó, nhưng mã đôi khi dẫn đến ngoại lệ. Tôi nghi ngờ đôi khi không thể phát hiện được - ít nhất là bởi họ - khi liên quan đến đa luồng, nhưng tôi không thể bình luận thêm vì tôi chưa xác định được vị trí lỗi của mình.
j đối thủ

Nhưng làm thế nào để giải quyết nó khi NullReferenceException xuất hiện trong usign HttpContext.Cản.Responce.Clear (). Nó không được giải quyết bằng bất kỳ giải pháp nào ở trên. bởi vì trong khi tạo đối tượng đối tượng của nó là HTTPContext thì đã xảy ra lỗi "Quá phân giải quá tải vì không thể truy cập 'Mới' chấp nhận Số lượng đối số này.
Sunny Sandeep

158

Nó có nghĩa là mã của bạn đã sử dụng một biến tham chiếu đối tượng được đặt thành null (nghĩa là nó không tham chiếu một thể hiện đối tượng thực tế).

Để ngăn chặn lỗi, các đối tượng có thể là null nên được kiểm tra null trước khi được sử dụng.

if (myvar != null)
{
    // Go ahead and use myvar
    myvar.property = ...
}
else
{
    // Whoops! myvar is null and cannot be used without first
    // assigning it to an instance reference
    // Attempting to use myvar here will result in NullReferenceException
}

96

Xin lưu ý rằng bất kể kịch bản nào, nguyên nhân luôn giống nhau trong .NET:

Bạn đang cố gắng sử dụng một biến tham chiếu có giá trị là Nothing/ null. Khi giá trị là Nothing/ nullcho biến tham chiếu, điều đó có nghĩa là nó không thực sự giữ một tham chiếu đến một thể hiện của bất kỳ đối tượng nào tồn tại trên heap.

Bạn không bao giờ được gán một cái gì đó cho biến, không bao giờ tạo một thể hiện của giá trị được gán cho biến hoặc bạn đặt biến bằng Nothing/ nullthủ công hoặc bạn đã gọi một hàm đặt biến thành Nothing/ nullcho bạn.


87

Một ví dụ về ngoại lệ này được đưa ra là: Khi bạn đang cố kiểm tra thứ gì đó, thì đó là null.

Ví dụ:

string testString = null; //Because it doesn't have a value (i.e. it's null; "Length" cannot do what it needs to do)

if (testString.Length == 0) // Throws a nullreferenceexception
{
    //Do something
} 

Thời gian chạy .NET sẽ đưa ra một NullReferenceException khi bạn cố gắng thực hiện một hành động trên một cái gì đó chưa được khởi tạo tức là mã ở trên.

So với một ArgumentNullException thường được sử dụng như một biện pháp phòng thủ nếu một phương thức mong đợi rằng những gì đang được truyền cho nó không phải là null.

Thông tin thêm có trong C # NullReferenceException và Null Parameter .


87

Cập nhật C # 8.0, 2019: Các loại tham chiếu Nullable

C # 8.0 giới thiệu các loại tham chiếu nullablecác loại tham chiếu không nullable . Vì vậy, chỉ các loại tham chiếu nullable phải được kiểm tra để tránh NullReferenceException .


Nếu bạn chưa khởi tạo một loại tham chiếu và bạn muốn đặt hoặc đọc một trong các thuộc tính của nó, nó sẽ đưa ra một NullReferenceException .

Thí dụ:

Person p = null;
p.Name = "Harry"; // NullReferenceException occurs here.

Bạn chỉ có thể tránh điều này bằng cách kiểm tra xem biến không phải là null:

Person p = null;
if (p!=null)
{
    p.Name = "Harry"; // Not going to run to this point
}

Để hiểu đầy đủ lý do tại sao một NullReferenceException bị ném, điều quan trọng là phải biết sự khác biệt giữa các loại giá trị và [loại tham chiếu] [3].

Vì vậy, nếu bạn đang xử lý các loại giá trị , NullReferenceExceptions không thể xảy ra. Mặc dù bạn cần phải cảnh giác khi làm việc với các loại tham chiếu !

Chỉ các loại tham chiếu, như tên đang gợi ý, có thể giữ các tham chiếu hoặc trỏ theo nghĩa đen vào không có gì (hoặc 'null'). Trong khi các loại giá trị luôn chứa một giá trị.

Các loại tham chiếu (những loại này phải được kiểm tra):

  • năng động
  • vật
  • chuỗi

Các loại giá trị (bạn chỉ có thể bỏ qua các loại này):

  • Các loại số
  • Các loại tích phân
  • Các loại dấu phẩy động
  • số thập phân
  • bool
  • Cấu trúc người dùng xác định

6
-1: vì câu hỏi là "NullReferenceException" là gì, các loại giá trị không liên quan.
John Saunders

21
@ John Saunders: Tôi không đồng ý. Là một nhà phát triển phần mềm, điều thực sự quan trọng là có thể phân biệt giữa các loại giá trị và tham chiếu. người khác cuối cùng sẽ kiểm tra nếu số nguyên là null.
Fabian Bigler

5
Đúng, chỉ là không trong bối cảnh của câu hỏi này.
John Saunders

4
Cảm ơn đã gợi ý. Tôi đã cải thiện nó một chút và thêm một ví dụ ở trên cùng. Tôi vẫn nghĩ rằng việc đề cập đến các loại Tham chiếu & Giá trị là hữu ích.
Fabian Bigler

5
Tôi nghĩ rằng bạn đã không thêm bất cứ điều gì không có trong các câu trả lời khác, vì câu hỏi đặt ra trước một loại tham chiếu.
John Saunders

78

Một trường hợp khác NullReferenceExceptionscó thể xảy ra là việc sử dụng (không chính xác) của astoán tử :

class Book {
    public string Name { get; set; }
}
class Car { }

Car mycar = new Car();
Book mybook = mycar as Book;   // Incompatible conversion --> mybook = null

Console.WriteLine(mybook.Name);   // NullReferenceException

Ở đây, BookCarlà các loại không tương thích; a Carkhông thể được chuyển đổi / đúc thành a Book. Khi diễn viên này thất bại, astrở lại null. Sử dụng mybooksau này gây ra a NullReferenceException.

Nói chung, bạn nên sử dụng một diễn viên hoặc as, như sau:

Nếu bạn đang mong đợi chuyển đổi loại luôn thành công (nghĩa là bạn biết đối tượng nào sẽ đi trước thời đại), thì bạn nên sử dụng một diễn viên:

ComicBook cb = (ComicBook)specificBook;

Nếu bạn không chắc chắn về loại, nhưng bạn muốn thử sử dụng nó như một loại cụ thể, thì hãy sử dụng as:

ComicBook cb = specificBook as ComicBook;
if (cb != null) {
   // ...
}

2
Điều này có thể xảy ra rất nhiều khi bỏ hộp một biến. Tôi thấy điều này thường xảy ra trong các trình xử lý sự kiện sau khi tôi thay đổi loại phần tử UI nhưng quên cập nhật mã phía sau.
Brendan

65

Bạn đang sử dụng đối tượng có chứa tham chiếu giá trị null. Vì vậy, nó đưa ra một ngoại lệ null. Trong ví dụ, giá trị chuỗi là null và khi kiểm tra độ dài của nó, ngoại lệ đã xảy ra.

Thí dụ:

string value = null;
if (value.Length == 0) // <-- Causes exception
{
    Console.WriteLine(value); // <-- Never reached
}

Lỗi ngoại lệ là:

Tình huống ngoại lệ không thể xử lí được:

System.NullReferenceException: Tham chiếu đối tượng không được đặt thành phiên bản của đối tượng. tại Chương trình.Main ()


1
Thật sâu sắc biết bao! Tôi chưa bao giờ coi hằng số 'null' là giá trị tham chiếu. Vậy đây là cách C # trừu tượng hóa một "NullPulum" hả? Khi tôi nhớ lại trong C ++, một NPE có thể được gây ra bởi việc hủy bỏ một con trỏ chưa được khởi tạo (nghĩa là loại ref trong c #) có giá trị mặc định là địa chỉ không được cấp cho quá trình đó (nhiều trường hợp điều này sẽ là 0, đặc biệt là trong các phiên bản sau của C ++ đã tự động khởi tạo, thuộc về HĐH - f với nó và die beeotch (hoặc chỉ bắt sigkill mà HĐH tấn công tiến trình của bạn)).
samis

64

Trong khi những gì gây ra NullReferenceExceptions và các cách tiếp cận để tránh / sửa một ngoại lệ như vậy đã được giải quyết trong các câu trả lời khác, điều mà nhiều lập trình viên chưa học được là làm thế nào để gỡ lỗi độc lập các ngoại lệ đó trong quá trình phát triển.

Trong Visual Studio, việc này thường dễ dàng nhờ Trình gỡ lỗi Visual Studio .


Trước tiên, hãy đảm bảo rằng lỗi chính xác sẽ bị bắt - xem Làm cách nào để cho phép tôi phá vỡ 'System.NullReferenceException' trong VS2010? Lưu ý 1

Sau đó, Bắt đầu với Gỡ lỗi (F5) hoặc Đính kèm [Trình gỡ lỗi VS] vào Quá trình đang chạy . Đôi khi nó có thể hữu ích để sử dụng Debugger.Break, nó sẽ nhắc khởi chạy trình gỡ lỗi.

Bây giờ, khi ném NullReferenceException (hoặc chưa xử lý), trình gỡ lỗi sẽ dừng (nhớ quy tắc được đặt ở trên?) Trên dòng xảy ra ngoại lệ. Đôi khi lỗi sẽ dễ dàng nhận ra.

Chẳng hạn, trong dòng sau đây, mã duy nhất có thể gây ra ngoại lệ là nếu myStringước tính thành null. Điều này có thể được xác minh bằng cách nhìn vào Cửa sổ xem hoặc chạy các biểu thức trong Cửa sổ ngay lập tức .

var x = myString.Trim();

Trong các trường hợp nâng cao hơn, chẳng hạn như sau, bạn sẽ cần sử dụng một trong các kỹ thuật trên (Xem hoặc Windows ngay lập tức) để kiểm tra các biểu thức để xác định xem str1là null hoặc nếu str2là null.

var x = str1.Trim() + str2.Trim();

Một khi vị trí ném ngoại lệ được đặt, thường là lý do tầm thường để tìm ra nơi giá trị null được giới thiệu [không chính xác] -

Dành thời gian cần thiết để hiểu nguyên nhân của ngoại lệ. Kiểm tra các biểu thức null. Kiểm tra các biểu thức trước đó có thể dẫn đến các biểu thức null như vậy. Thêm điểm dừng và bước qua chương trình khi thích hợp. Sử dụng trình gỡ lỗi.


1 Nếu Break on Ném quá hung hăng và trình gỡ lỗi dừng trên NPE trong thư viện .NET hoặc bên thứ 3, Break on User-Unhandled có thể được sử dụng để hạn chế các ngoại lệ bị bắt. Ngoài ra, VS2012 cũng giới thiệu Just My Code mà tôi khuyên bạn cũng nên bật.

Nếu bạn đang gỡ lỗi khi kích hoạt Just My Code, hành vi sẽ hơi khác. Khi Just My Code được bật, trình gỡ lỗi bỏ qua các ngoại lệ thời gian chạy ngôn ngữ chung (CLR) cơ hội đầu tiên được đưa ra ngoài Mã của tôi và không chuyển qua Mã của tôi


59

Simon Mourier đã đưa ra ví dụ này :

object o = null;
DateTime d = (DateTime)o;  // NullReferenceException

trong đó một chuyển đổi unboxing (truyền) từ object (hoặc từ một trong các lớp System.ValueTypehoặc System.Enum, hoặc từ một loại giao diện) sang một loại giá trị (khác Nullable<>) tự nó cung cấp cho NullReferenceException.

Theo hướng khác, một chuyển đổi quyền anh từ một Nullable<>loại HasValuetương đương false với một loại tham chiếu, có thể đưa ra một nulltham chiếu mà sau đó có thể dẫn đến một NullReferenceException. Ví dụ kinh điển là:

DateTime? d = null;
var s = d.ToString();  // OK, no exception (no boxing), returns ""
var t = d.GetType();   // Bang! d is boxed, NullReferenceException

Đôi khi quyền anh xảy ra theo một cách khác. Ví dụ với phương thức mở rộng không chung chung này:

public static void MyExtension(this object x)
{
  x.ToString();
}

đoạn mã sau sẽ có vấn đề:

DateTime? d = null;
d.MyExtension();  // Leads to boxing, NullReferenceException occurs inside the body of the called method, not here.

Những trường hợp này phát sinh do các quy tắc đặc biệt mà thời gian chạy sử dụng khi các Nullable<>trường hợp quyền anh .


42

Thêm trường hợp khi tên lớp cho thực thể được sử dụng trong khung thực thể giống như tên lớp cho tệp mã phía sau của biểu mẫu web.

Giả sử bạn có một biểu mẫu web Contact.aspx có lớp codebehind là Liên hệ và bạn có một tên thực thể Liên hệ.

Sau đó, đoạn mã sau sẽ đưa ra một NullReferenceException khi bạn gọi bối cảnh.SaveChanges ()

Contact contact = new Contact { Name = "Abhinav"};
var context = new DataContext();
context.Contacts.Add(contact);
context.SaveChanges(); // NullReferenceException at this line

Để hoàn thiện lớp DataContext

public class DataContext : DbContext 
{
    public DbSet<Contact> Contacts {get; set;}
}

và Liên hệ lớp thực thể. Đôi khi các lớp thực thể là các lớp một phần để bạn cũng có thể mở rộng chúng trong các tệp khác.

public partial class Contact 
{
    public string Name {get; set;}
}

Lỗi xảy ra khi cả lớp thực thể và lớp cơ sở trong cùng một không gian tên. Để sửa lỗi này, đổi tên lớp thực thể hoặc lớp codebehind cho Contact.aspx.

Lý do tôi vẫn không chắc chắn về lý do. Nhưng bất cứ khi nào bất kỳ lớp thực thể nào sẽ mở rộng System.Web.UI.Page thì lỗi này xảy ra.

Để thảo luận, hãy xem NullReferenceException trong DbContext.saveChanges ()


41

Một trường hợp chung khác mà người ta có thể nhận được ngoại lệ này liên quan đến các lớp chế nhạo trong quá trình kiểm tra đơn vị. Bất kể khung mô phỏng đang được sử dụng, bạn phải đảm bảo rằng tất cả các mức phù hợp của hệ thống phân cấp lớp được chế giễu đúng. Cụ thể, tất cả các thuộc tính HttpContextđược tham chiếu bởi mã được kiểm tra phải được chế giễu.

Xem " NullReferenceException được ném khi kiểm tra AuthorizationAttribution tùy chỉnh " để biết ví dụ hơi dài dòng.


40

Tôi có một quan điểm khác nhau để trả lời điều này. Loại câu trả lời này "tôi có thể làm gì khác để tránh nó? "

Khi làm việc trên các lớp khác nhau , ví dụ như trong ứng dụng MVC, bộ điều khiển cần các dịch vụ để gọi các hoạt động kinh doanh. Trong các trường hợp như vậy, Dependency Injection Container có thể được sử dụng để khởi tạo các dịch vụ nhằm tránh NullReferenceException . Vì vậy, điều đó có nghĩa là bạn không cần phải lo lắng về việc kiểm tra null và chỉ cần gọi các dịch vụ từ bộ điều khiển như thể chúng sẽ luôn có sẵn (và được khởi tạo) dưới dạng đơn lẻ hoặc nguyên mẫu.

public class MyController
{
    private ServiceA serviceA;
    private ServiceB serviceB;

    public MyController(ServiceA serviceA, ServiceB serviceB)
    {
        this.serviceA = serviceA;
        this.serviceB = serviceB;
    }

    public void MyMethod()
    {
        // We don't need to check null because the dependency injection container 
        // injects it, provided you took care of bootstrapping it.
        var someObject = serviceA.DoThis();
    }
}

6
-1: điều này chỉ xử lý một kịch bản duy nhất - đó là các phụ thuộc chưa được khởi tạo. Đây là một kịch bản thiểu số cho NullReferenceException. Hầu hết các trường hợp là sự hiểu lầm đơn giản về cách các đối tượng làm việc. Tiếp theo thường xuyên nhất là các tình huống khác trong đó nhà phát triển cho rằng đối tượng sẽ được khởi tạo tự động.
John Saunders

Việc tiêm phụ thuộc thường không được sử dụng để tránh NullReferenceException. Tôi không tin rằng bạn đã tìm thấy một kịch bản chung ở đây. Trong mọi trường hợp, nếu bạn chỉnh sửa câu trả lời của mình thành nhiều hơn theo kiểu stackoverflow.com/a/15232518/76337 , thì tôi sẽ xóa downvote.
John Saunders

38

Về vấn đề "tôi nên làm gì với nó" , có thể có nhiều câu trả lời.

Một cách "chính thức" hơn để ngăn chặn các điều kiện lỗi như vậy trong khi phát triển là áp dụng thiết kế theo hợp đồng trong mã của bạn. Điều này có nghĩa là bạn cần thiết lập các bất biến lớp và / hoặc thậm chí các điều kiện tiên quyết về chức năng / phương thức và hậu điều kiện trên hệ thống của bạn, trong khi phát triển.

Nói tóm lại, các bất biến lớp đảm bảo rằng sẽ có một số ràng buộc trong lớp của bạn sẽ không bị vi phạm trong sử dụng bình thường (và do đó, lớp sẽ không ở trạng thái không nhất quán). Điều kiện tiên quyết có nghĩa là dữ liệu được cung cấp làm đầu vào cho một hàm / phương thức phải tuân theo một số ràng buộc được đặt và không bao giờ vi phạm chúng, và hậu điều kiện có nghĩa là đầu ra của hàm / phương thức phải tuân theo các ràng buộc đã đặt lại mà không bao giờ vi phạm chúng. Các điều kiện hợp đồng không bao giờ bị vi phạm trong khi thực hiện chương trình không có lỗi, do đó, thiết kế theo hợp đồng được kiểm tra trong thực tế ở chế độ gỡ lỗi, trong khi bị vô hiệu hóa trong các bản phát hành , để tối đa hóa hiệu suất hệ thống được phát triển.

Bằng cách này, bạn có thể tránh NullReferenceExceptioncác trường hợp là kết quả vi phạm các ràng buộc được đặt. Ví dụ: nếu bạn sử dụng một Xthuộc tính đối tượng trong một lớp và sau đó cố gắng gọi một trong các phương thức của nó và Xcó giá trị null, thì điều này sẽ dẫn đến NullReferenceException:

public X { get; set; }

public void InvokeX()
{
    X.DoSomething(); // if X value is null, you will get a NullReferenceException
}

Nhưng nếu bạn đặt "thuộc tính X không bao giờ có giá trị null" làm điều kiện tiên quyết của phương thức, thì bạn có thể ngăn tình huống được mô tả trước đây:

//Using code contracts:
[ContractInvariantMethod]
protected void ObjectInvariant () 
{
    Contract.Invariant ( X != null );
    //...
}

Vì lý do này, dự án Hợp đồng mã tồn tại cho các ứng dụng .NET.

Ngoài ra, thiết kế theo hợp đồng có thể được áp dụng bằng cách sử dụng các xác nhận .

CẬP NHẬT: Điều đáng nói là thuật ngữ này được đặt ra bởi Bertrand Meyer liên quan đến thiết kế ngôn ngữ lập trình Eiffel của ông .


2
Tôi nghĩ để thêm điều này vì không ai đề cập đến điều này, và theo như nó tồn tại như một cách tiếp cận, ý định của tôi là làm phong phú thêm chủ đề.
Nick Louloudakis

2
Cảm ơn bạn đã làm phong phú chủ đề. Tôi đã đưa ra ý kiến ​​của tôi về sự bổ sung của bạn. Bây giờ những người khác có thể làm như vậy.
John Saunders

2
Tôi nghĩ rằng đây là một bổ sung đáng giá cho chủ đề cho rằng đây là một chủ đề được xem cao. Tôi đã nghe nói về các hợp đồng mã trước đây và đây là một lời nhắc tốt để xem xét sử dụng chúng.
VoteCoffee

36

A NullReferenceExceptionbị ném khi chúng ta đang cố truy cập vào Thuộc tính của đối tượng null hoặc khi giá trị chuỗi trở nên trống rỗng và chúng ta đang cố gắng truy cập các phương thức chuỗi.

Ví dụ:

  1. Khi một phương thức chuỗi của một chuỗi rỗng được truy cập:

    string str = string.Empty;
    str.ToLower(); // throw null reference exception
  2. Khi một thuộc tính của một đối tượng null được truy cập:

    Public Class Person {
        public string Name { get; set; }
    }
    Person objPerson;
    objPerson.Name  /// throw Null refernce Exception 

2
Điều này là không chính xác. String.Empty.ToLower()sẽ không ném ngoại lệ tham chiếu null. Nó đại diện cho một chuỗi thực tế, mặc dù một chuỗi trống (nghĩa là ""). Vì điều này có một đối tượng để gọi ToLower(), nên sẽ không có ý nghĩa gì khi ném ngoại lệ tham chiếu null vào đó.
Kjartan

31

TL; DR: Hãy thử sử dụng Html.Partialthay vìRenderpage


Tôi đã nhận được Object reference not set to an instance of an objectkhi tôi cố gắng hiển thị Chế độ xem trong Chế độ xem bằng cách gửi Mô hình, như thế này:

@{
    MyEntity M = new MyEntity();
}
@RenderPage("_MyOtherView.cshtml", M); // error in _MyOtherView, the Model was Null

Gỡ lỗi cho thấy mô hình là Null bên trong MyOtherView. Cho đến khi tôi đổi nó thành:

@{
    MyEntity M = new MyEntity();
}
@Html.Partial("_MyOtherView.cshtml", M);

Va no đa hoạt động.

Hơn nữa, lý do tôi không phải Html.Partialbắt đầu là vì Visual Studio đôi khi ném các dòng nguệch ngoạc trông có lỗi Html.Partialnếu nó nằm trong một foreachvòng lặp được xây dựng khác , mặc dù nó không thực sự là một lỗi:

@inherits System.Web.Mvc.WebViewPage
@{
    ViewBag.Title = "Entity Index";
    List<MyEntity> MyEntities = new List<MyEntity>();
    MyEntities.Add(new MyEntity());
    MyEntities.Add(new MyEntity());
    MyEntities.Add(new MyEntity());
}
<div>
    @{
        foreach(var M in MyEntities)
        {
            // Squiggly lines below. Hovering says: cannot convert method group 'partial' to non-delegate type Object, did you intend to envoke the Method?
            @Html.Partial("MyOtherView.cshtml");
        }
    }
</div>

Nhưng tôi đã có thể chạy ứng dụng mà không gặp vấn đề gì với "lỗi" này. Tôi đã có thể thoát khỏi lỗi bằng cách thay đổi cấu trúc của foreachvòng lặp để trông như thế này:

@foreach(var M in MyEntities){
    ...
}

Mặc dù tôi có cảm giác đó là vì Visual Studio đã đọc sai các ký hiệu và dấu ngoặc.


Bạn muốn Html.Partial, không phải@Html.Partial
John Saunders

Ngoài ra, vui lòng hiển thị dòng nào đã ném ngoại lệ, và tại sao.
John Saunders

Lỗi xảy ra trong MyOtherView.cshtml, mà tôi không bao gồm ở đây, vì Mô hình không được gửi đúng cách ( Nullvì vậy), vì vậy tôi biết lỗi là do tôi đã gửi Mô hình vào.
Travis Heeter

22

Bạn có thể làm gì về nó?

Có rất nhiều câu trả lời hay ở đây giải thích thế nào là một tài liệu tham khảo null và cách gỡ lỗi nó. Nhưng có rất ít về cách ngăn chặn vấn đề hoặc ít nhất là làm cho nó dễ nắm bắt hơn.

Kiểm tra đối số

Ví dụ, các phương thức có thể kiểm tra các đối số khác nhau để xem liệu chúng có rỗng không và ném ra ArgumentNullException, một ngoại lệ rõ ràng được tạo ra cho mục đích chính xác này.

Hàm tạo cho ArgumentNullExceptionchẵn lấy tên của tham số và thông báo làm đối số để bạn có thể cho nhà phát triển biết chính xác vấn đề là gì.

public void DoSomething(MyObject obj) {
    if(obj == null) 
    {
        throw new ArgumentNullException("obj", "Need a reference to obj.");
    }
}

Sử dụng công cụ

Ngoài ra còn có một số thư viện có thể giúp đỡ. Ví dụ: "Chia sẻ lại" có thể cung cấp cho bạn các cảnh báo trong khi bạn đang viết mã, đặc biệt nếu bạn sử dụng thuộc tính của chúng: NotNullAttribution

Có "Hợp đồng mã Microsoft" nơi bạn sử dụng cú pháp như Contract.Requires(obj != null)cung cấp cho bạn thời gian chạy và kiểm tra biên dịch: Giới thiệu Hợp đồng mã .

Ngoài ra còn có "PostSharp" cho phép bạn chỉ sử dụng các thuộc tính như thế này:

public void DoSometing([NotNull] obj)

Bằng cách thực hiện điều đó và làm cho PostSharp trở thành một phần của quá trình xây dựng của bạn objsẽ được kiểm tra null khi chạy. Xem: Kiểm tra null PostSharp

Giải pháp mã đồng bằng

Hoặc bạn luôn có thể viết mã phương pháp của riêng bạn bằng cách sử dụng mã cũ đơn giản. Ví dụ ở đây là một cấu trúc mà bạn có thể sử dụng để bắt các tham chiếu null. Nó được mô phỏng theo khái niệm tương tự như Nullable<T>:

[System.Diagnostics.DebuggerNonUserCode]
public struct NotNull<T> where T: class
{
    private T _value;

    public T Value
    {
        get
        {
            if (_value == null)
            {
                throw new Exception("null value not allowed");
            }

            return _value;
        }
        set
        {
            if (value == null)
            {
                throw new Exception("null value not allowed.");
            }

            _value = value;
        }
    }

    public static implicit operator T(NotNull<T> notNullValue)
    {
        return notNullValue.Value;
    }

    public static implicit operator NotNull<T>(T value)
    {
        return new NotNull<T> { Value = value };
    }
}

Bạn sẽ sử dụng rất giống với cách bạn sẽ sử dụng Nullable<T>, ngoại trừ với mục tiêu hoàn thành chính xác điều ngược lại - không cho phép null. Dưới đây là một số ví dụ:

NotNull<Person> person = null; // throws exception
NotNull<Person> person = new Person(); // OK
NotNull<Person> person = GetPerson(); // throws exception if GetPerson() returns null

NotNull<T>được mặc nhiên chuyển đến và từ Tđó bạn có thể sử dụng nó ở bất cứ đâu bạn cần. Ví dụ: bạn có thể truyền một Personđối tượng cho một phương thức có một NotNull<Person>:

Person person = new Person { Name = "John" };
WriteName(person);

public static void WriteName(NotNull<Person> person)
{
    Console.WriteLine(person.Value.Name);
}

Như bạn có thể thấy ở trên như với nullable, bạn sẽ truy cập giá trị cơ bản thông qua thuộc Valuetính. Ngoài ra, bạn có thể sử dụng một diễn viên rõ ràng hoặc ẩn, bạn có thể xem một ví dụ với giá trị trả về bên dưới:

Person person = GetPerson();

public static NotNull<Person> GetPerson()
{
    return new Person { Name = "John" };
}

Hoặc thậm chí bạn có thể sử dụng nó khi phương thức chỉ trả về T(trong trường hợp này Person) bằng cách thực hiện cast. Ví dụ: đoạn mã sau sẽ giống như đoạn mã trên:

Person person = (NotNull<Person>)GetPerson();

public static Person GetPerson()
{
    return new Person { Name = "John" };
}

Kết hợp với phần mở rộng

Kết hợp NotNull<T>với một phương pháp mở rộng và bạn có thể bao quát nhiều tình huống hơn nữa. Dưới đây là một ví dụ về phương thức mở rộng có thể trông như thế nào:

[System.Diagnostics.DebuggerNonUserCode]
public static class NotNullExtension
{
    public static T NotNull<T>(this T @this) where T: class
    {
        if (@this == null)
        {
            throw new Exception("null value not allowed");
        }

        return @this;
    }
}

Và đây là một ví dụ về cách nó có thể được sử dụng:

var person = GetPerson().NotNull();

GitHub

Để bạn tham khảo, tôi đã cung cấp mã ở trên trên GitHub, bạn có thể tìm thấy nó tại:

https://github.com/luisperezphd/NotNull

Tính năng ngôn ngữ liên quan

C # 6.0 đã giới thiệu "toán tử có điều kiện null" giúp điều này một chút. Với tính năng này, bạn có thể tham chiếu các đối tượng lồng nhau và nếu bất kỳ một trong số chúng là nulltoàn bộ biểu thức trả về null.

Điều này làm giảm số lượng kiểm tra null bạn phải làm trong một số trường hợp. Cú pháp là đặt một dấu hỏi trước mỗi dấu chấm. Lấy mã sau đây làm ví dụ:

var address = country?.State?.County?.City;

Hãy tưởng tượng rằng đó countrylà một đối tượng của loại Countrycó một thuộc tính được gọi Statevà như vậy. Nếu country, State, County, hoặc Citynullsau đó address will be. Therefore you only have to check whetherđịa chỉ isnull`.

Đây là một tính năng tuyệt vời, nhưng nó cung cấp cho bạn ít thông tin hơn. Nó không làm cho nó rõ ràng trong số 4 là null.

Được xây dựng như Nullable?

C # có một tốc ký tuyệt vời Nullable<T>, bạn có thể làm cho một cái gì đó không thể bằng cách đặt một dấu hỏi sau kiểu như vậy int?.

Sẽ thật tuyệt nếu C # có một cái gì đó giống như NotNull<T>cấu trúc ở trên và có một tốc ký tương tự, có thể là dấu chấm than (!) Để bạn có thể viết một cái gì đó như : public void WriteName(Person! person).


2
Không bao giờ ném NullReferenceException
John Saunders

@JohnSaunders dám hỏi tại sao? (Nghiêm túc mặc dù tại sao?)
Luis Perez

2
NullReferenceException có nghĩa là được ném bởi CLR. Nó có nghĩa là một tham chiếu đến null đã xảy ra. Điều đó không có nghĩa là một tham chiếu đến null sẽ xảy ra ngoại trừ việc bạn khéo léo kiểm tra trước.
John Saunders

Tôi thấy quan điểm của bạn về việc đó sẽ gây nhầm lẫn như thế nào. Tôi đã cập nhật nó thành một ngoại lệ thường xuyên cho ví dụ này và một ngoại lệ tùy chỉnh trong GitHub.
Luis Perez

Câu trả lời tuyệt vời cho một câu hỏi cơ bản như vậy. Nó không quá tệ khi mã của bạn bị lỗi. Thật kinh khủng khi nó đến từ sâu bên trong một thư viện thương mại của bên thứ ba mà bạn đang dựa vào và bộ phận hỗ trợ khách hàng cứ khăng khăng rằng đó phải là mã của bạn gây ra sự cố. Và bạn không hoàn toàn chắc chắn là không và toàn bộ dự án sẽ dừng lại .. Tôi thực sự nghĩ rằng điều này có thể tạo ra một văn bia thích hợp cho bia mộ của tôi: "Tham chiếu đối tượng không được đặt thành một thể hiện của một đối tượng."
Darrel Lee

10

Thật thú vị, không có câu trả lời nào trên trang này đề cập đến hai trường hợp cạnh, hy vọng không ai bận tâm nếu tôi thêm chúng:

Trường hợp cạnh # 1: truy cập đồng thời vào Từ điển

Từ điển chung trong .NET không an toàn cho luồng và đôi khi chúng có thể ném một NullReferencehoặc thậm chí (thường xuyên hơn) KeyNotFoundExceptionkhi bạn cố truy cập khóa từ hai luồng đồng thời. Ngoại lệ là khá sai lệch trong trường hợp này.

Trường hợp cạnh # 2: mã không an toàn

Nếu a NullReferenceExceptionbị ném bởi unsafemã, bạn có thể nhìn vào các biến con trỏ của mình và kiểm tra xem chúng có IntPtr.Zerohay không. Đó là điều tương tự ("ngoại lệ con trỏ null"), nhưng trong mã không an toàn, các biến thường được chuyển thành các loại giá trị / mảng, v.v., và bạn đập đầu vào tường, tự hỏi làm thế nào một loại giá trị có thể ném cái này ngoại lệ.

(Một lý do khác để không sử dụng mã không an toàn trừ khi bạn cần nó, nhân tiện)


5
Ví dụ từ điển của bạn không phải là một trường hợp cạnh. Nếu đối tượng không phải là luồng an toàn, thì việc sử dụng nó từ nhiều luồng sẽ tạo ra kết quả ngẫu nhiên. Ví dụ mã không an toàn của bạn khác với nullcách nào?
John Saunders

10

Bạn có thể sửa lỗi NullReferenceException một cách rõ ràng bằng cách sử dụng các toán tử điều kiện Null trong c # 6 và viết ít mã hơn để xử lý kiểm tra null.

Nó được sử dụng để kiểm tra null trước khi thực hiện thao tác truy cập thành viên (?.) Hoặc chỉ mục (? [).

Thí dụ

  var name = p?.Spouse?.FirstName;

tương đương với:

    if (p != null)
    {
        if (p.Spouse != null)
        {
            name = p.Spouse.FirstName;
        }
    }

Kết quả là tên sẽ là null khi p là null hoặc khi p.Spouse là null.

Nếu không, tên biến sẽ được gán giá trị của p.Spouse.FirstName.

Để biết thêm chi tiết: Toán tử không có điều kiện


9

Dòng lỗi "Tham chiếu đối tượng không được đặt thành phiên bản của đối tượng." Nói rằng bạn chưa gán đối tượng đối tượng cho tham chiếu đối tượng và bạn vẫn đang truy cập các phương thức / phương thức của đối tượng đó.

ví dụ: giả sử bạn có một lớp được gọi là myClass và nó chứa một thuộc tính prop1.

public Class myClass
{
   public int prop1 {get;set;}
}

Bây giờ bạn đang truy cập prop1 này trong một số lớp khác như dưới đây:

public class Demo
{
     public void testMethod()
     {
        myClass ref = null;
        ref.prop1 = 1;  //This line throws error
     }
}

dòng trên ném lỗi vì tham chiếu của lớp myClass được khai báo nhưng không được khởi tạo hoặc một thể hiện của đối tượng không được gán cho tham chiếu của lớp đó.

Để sửa lỗi này, bạn phải khởi tạo (gán đối tượng cho tham chiếu của lớp đó).

public class Demo
{
     public void testMethod()
     {
        myClass ref = null;
        ref = new myClass();
        ref.prop1 = 1;  
     }
}

4

Tham chiếu NullReferenceException hoặc Object không được đặt thành phiên bản của đối tượng xảy ra khi một đối tượng của lớp bạn đang cố sử dụng không được khởi tạo. Ví dụ:

Giả sử rằng bạn có một lớp có tên là Sinh viên.

public class Student
{
    private string FirstName;
    private string LastName;
    public string GetFullName()
    {
        return FirstName + LastName;
    }
}

Bây giờ, hãy xem xét một lớp học khác mà bạn đang cố lấy lại tên đầy đủ của học sinh.

public class StudentInfo
{      
    public string GetStudentName()
    {
        Student s;
        string fullname = s.GetFullName();
        return fullname;
    }        
}

Như đã thấy trong đoạn mã trên, câu lệnh Student s - chỉ khai báo biến loại Sinh viên, lưu ý rằng lớp Sinh viên không được khởi tạo tại thời điểm này. Do đó, khi câu lệnh s.GetFullName () được thực thi, nó sẽ ném NullReferenceException.


3

Vâng, trong điều kiện đơn giản:

Bạn đang cố gắng truy cập một đối tượng không được tạo hoặc hiện không có trong bộ nhớ.

Vậy làm thế nào để giải quyết điều này:

  1. Gỡ lỗi và để trình gỡ lỗi phá vỡ ... Nó sẽ trực tiếp đưa bạn đến biến bị hỏng ... Bây giờ nhiệm vụ của bạn chỉ là sửa lỗi này .. Sử dụng từ khóa mới ở vị trí thích hợp.

  2. Nếu nó được gây ra trên một số lệnh cơ sở dữ liệu vì đối tượng không có mặt thì tất cả những gì bạn cần làm là thực hiện kiểm tra null và xử lý nó:

    if (i == null) {
        // Handle this
    }
  3. Cái khó nhất .. nếu GC đã thu thập đối tượng rồi ... Điều này thường xảy ra nếu bạn đang cố gắng tìm một đối tượng bằng chuỗi ... Đó là, tìm nó theo tên của đối tượng thì có thể xảy ra rằng GC có thể đã xảy ra làm sạch nó ... Điều này rất khó tìm và sẽ trở thành một vấn đề ... Cách tốt hơn để giải quyết vấn đề này là kiểm tra null bất cứ khi nào cần thiết trong quá trình phát triển. Điều này sẽ giúp bạn tiết kiệm rất nhiều thời gian.

Bằng cách tìm theo tên tôi có nghĩa là một số khung cho phép bạn FIndObjects sử dụng chuỗi và mã có thể trông như thế này: FindObject ("ObjectName");


3
Nếu bạn có một tham chiếu đến một đối tượng, thì GC không bao giờ xóa sạch nó
John Saunders

2
nếu bạn sử dụng những thứ như FindObject ("Tên của đối tượng") thì không có cách nào mà GC sẽ biết trước rằng bạn sẽ từ chối đối tượng đó .. đây là những gì đang cố gắng khám phá .. những điều này xảy ra trong thời gian chạy
Akash Gutha

2
Có một số khung cung cấp chức năng này trong C # như Unity. câu hỏi không có gì liên quan đến BCl. Tìm kiếm trên Internet trước khi phê bình có rất nhiều chức năng như chúng và để biết thông tin loại bạn thậm chí tôi sử dụng nó hàng ngày. Bây giờ xin vui lòng cho tôi biết làm thế nào để câu trả lời không làm bất cứ điều gì.
Akash Gutha

2
docs.unity3d.com/ScriptReference/... kiểm tra liên kết và chính xác urself mr.expert: p
Akash Gutha

Các ví dụ tôi thấy trong liên kết của bạn chỉ định kết quả của GameObject.Find cho trường thành viên. Đó là một tài liệu tham khảo và GC sẽ không thu thập nó cho đến khi đối tượng chứa được thu thập.
John Saunders

1

Theo nghĩa đen, cách dễ nhất để sửa lỗi NullReferenceExellect có hai cách. Nếu bạn có GameObject chẳng hạn với tập lệnh được đính kèm và một biến có tên rb (Rigidbody) thì biến này sẽ bắt đầu null khi bạn bắt đầu trò chơi của mình.
Đây là lý do tại sao bạn nhận được NullReferenceExellect vì máy tính không có dữ liệu được lưu trữ trong biến đó.

Tôi sẽ sử dụng biến RigidBody làm ví dụ.
Chúng tôi có thể thêm dữ liệu thực sự dễ dàng thực sự theo một số cách:

  1. Thêm RigidBody vào đối tượng của bạn bằng AddComponent> Vật lý> Rigidbody
    Sau đó đi vào tập lệnh của bạn và nhập rb = GetComponent<Rigidbody>();
    Dòng mã này hoạt động tốt nhất theo chức năng Start()hoặc Awake()chức năng của bạn .
  2. Bạn có thể thêm một thành phần theo chương trình và gán biến cùng lúc với một dòng mã: rb = AddComponent<RigidBody>();

Ghi chú thêm: Nếu bạn muốn thống nhất thêm một thành phần vào đối tượng của mình và bạn có thể đã quên thêm một thành phần, bạn có thể nhập [RequireComponent(typeof(RigidBody))]phía trên khai báo lớp của bạn (khoảng trắng bên dưới tất cả các cách sử dụng của bạn).
Thưởng thức và vui chơi làm trò chơi!


-1

Nếu chúng ta xem xét các tình huống phổ biến trong đó ngoại lệ này có thể được ném, truy cập các thuộc tính héo đối tượng ở trên cùng.

Ví dụ:

string postalcode=Customer.Address.PostalCode; 
//if customer or address is null , this will through exeption

ở đây, nếu địa chỉ là null, thì bạn sẽ nhận được NullReferenceException.

Vì vậy, như một thông lệ, chúng ta nên luôn luôn sử dụng kiểm tra null, trước khi truy cập các thuộc tính trong các đối tượng đó (đặc biệt chung chung)

string postalcode=Customer?.Address?.PostalCode;
//if customer or address is null , this will return null, without through a exception

-3

Đây là một ngoại lệ tham chiếu Null . Như Microsoft tuyên bố-

Một ngoại lệ NullReferenceException được đưa ra khi bạn cố gắng truy cập một thành viên của loại có giá trị là null.

Điều đó nghĩa là gì?

Điều đó có nghĩa là nếu bất kỳ thành viên nào không giữ bất kỳ giá trị nào và chúng tôi đang khiến thành viên đó thực hiện một số nhiệm vụ nhất định thì hệ thống chắc chắn sẽ ném một tin nhắn và nói-

Hãy chờ đợi, thành viên đó không có giá trị nên không thể thực hiện nhiệm vụ mà bạn đang bàn giao.

Chính ngoại lệ nói rằng một cái gì đó đang được đề cập nhưng giá trị của nó không được đặt. Vì vậy, điều này biểu thị rằng nó chỉ xảy ra trong khi sử dụng các loại tham chiếu làm Loại giá trị là không thể rỗng.

NullReferenceException sẽ không xảy ra nếu chúng ta đang sử dụng các thành viên loại Giá trị.

class Program
{
    static void Main(string[] args)
    {
        string str = null;
        Console.WriteLine(str.Length);
        Console.ReadLine();
    }
}

Đoạn mã trên hiển thị chuỗi đơn giản được gán với giá trị null .

Bây giờ, khi tôi cố gắng in độ dài của chuỗi str , tôi nhận được một ngoại lệ chưa được xử lý của loại thông báo 'System.NullReferenceException'str thành viên đang trỏ đến null và không thể có độ dài null.

' NullReferenceException ' cũng xảy ra khi chúng ta quên khởi tạo một kiểu tham chiếu.

Giả sử tôi có một phương thức lớp và thành viên trong đó. Tôi đã không ngay lập tức lớp học của tôi mà chỉ đặt tên lớp học của tôi. Bây giờ nếu tôi cố gắng sử dụng phương thức, trình biên dịch sẽ đưa ra lỗi hoặc đưa ra cảnh báo (tùy thuộc vào trình biên dịch).

class Program
{
    static void Main(string[] args)
    {
        MyClass1 obj;
        obj.foo();  //Use of unassigned local variable 'obj'
    }
}

public class MyClass1
{
    internal void foo()
    {
        Console.WriteLine("hello from foo");

    }
}

Trình biên dịch cho mã ở trên làm phát sinh lỗi rằng biến obj không được gán mà biểu thị rằng biến của chúng ta có giá trị null hoặc không có gì. Trình biên dịch cho mã ở trên làm phát sinh lỗi rằng biến obj không được gán mà biểu thị rằng biến của chúng ta có giá trị null hoặc không có gì.

Tại sao nó xảy ra?

  • NullReferenceException phát sinh do lỗi của chúng tôi vì không kiểm tra giá trị của đối tượng. Chúng ta thường bỏ chọn các giá trị đối tượng trong quá trình phát triển mã.

  • Nó cũng phát sinh khi chúng ta quên khởi tạo các đối tượng của chúng ta. Sử dụng các phương thức, thuộc tính, bộ sưu tập, vv có thể trả về hoặc đặt giá trị null cũng có thể là nguyên nhân của ngoại lệ này.

Làm thế nào nó có thể tránh được?

Có nhiều cách và phương pháp khác nhau để tránh ngoại lệ nổi tiếng này:

  1. Kiểm tra rõ ràng: Chúng ta nên tuân thủ truyền thống kiểm tra các đối tượng, thuộc tính, phương thức, mảng và bộ sưu tập cho dù chúng là null. Điều này có thể được thực hiện đơn giản bằng cách sử dụng các câu lệnh có điều kiện như if-other if-other, v.v.

  2. Xử lý ngoại lệ: Một trong những cách quan trọng để quản lý ngoại lệ này. Sử dụng các khối thử-bắt-cuối đơn giản, chúng ta có thể kiểm soát ngoại lệ này và cũng duy trì nhật ký của nó. Điều này có thể rất hữu ích khi ứng dụng của bạn đang ở giai đoạn sản xuất.

  3. Toán tử Null: Toán tử Null Coalescing và toán tử điều kiện null cũng có thể được sử dụng một cách hữu ích trong khi đặt giá trị cho các đối tượng, biến, thuộc tính và trường.

  4. Trình gỡ lỗi: Đối với các nhà phát triển, chúng tôi có vũ khí lớn để gỡ lỗi với chúng tôi. Nếu chúng ta phải đối mặt với NullReferenceException trong quá trình phát triển, chúng ta có thể sử dụng trình gỡ lỗi để lấy nguồn của ngoại lệ.

  5. Phương thức dựng sẵn: Các phương thức hệ thống như GetValueOrDefault (), IsNullOrWhiteSpace () và IsNullorEmpty () kiểm tra null và gán giá trị mặc định nếu có giá trị null.

Có rất nhiều câu trả lời tốt ở đây. Bạn cũng có thể kiểm tra mô tả chi tiết hơn với các ví dụ trên blog của tôi .

Hy vọng điều này sẽ giúp quá!


Về cơ bản, bạn đã sao chép một nửa bài đăng trên blog đó và không thêm gì mới mà câu trả lời hiện tại không giải quyết.
CodeCaster

@codecaster Có phải là sao chép khi bạn viết lại một bản tóm tắt từ blog của riêng bạn. Tôi biết không có gì mới trong câu trả lời của tôi và không có gì mới mà những câu trả lời trước không có nhưng tôi muốn đóng góp theo cách tinh vi hơn và để người khác hiểu theo cách tôi hiểu. Sẽ vui mừng ngay cả khi nó giúp một người duy nhất. Trong đức tin tốt.
Wasim

-4

Nếu ai đó nhận được thông báo này trong khi lưu hoặc biên dịch bản dựng, chỉ cần đóng tất cả các tệp và sau đó mở bất kỳ tệp nào để biên dịch và lưu.

Đối với tôi lý do là tôi đã đổi tên tập tin và tập tin cũ vẫn mở.

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.