it-swarm-vi.tech

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

Tôi có một số mã và khi nó thực thi, nó sẽ ném 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?

1877
John Saunders

Ngoại lệ NullReference - Visual Basic

NullReference Exception cho Visual Basic không khác với cái 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 mức độ liên quan bằng cách sử dụng loại trong các 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 là rất có khả năng được liệt kê ở đây.

Lưu ý:

  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 phải 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. Đây là một trong những điều sau đây:

  • 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 là 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ị các giá trị của chúng - thủ phạm sẽ là Nothing.

IDE debug display

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 là Nothing. Đây là những gì bạn muốn bởi vì nó sẽ xác định chính xác vị trí của vấn đề và cho phép bạn xác định đối tượng gây ra nó.

Một MsgBox trong Catch hiển thị Error while... sẽ giúp ích rất ít. Phương pháp này cũng dẫn đến rất tệ Các câu hỏi về Stack Overflow, 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í là 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à Dim không tạo ra CashRegister object; nó chỉ khai báo một biến có tên reg của Loạ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

Toán tử New thường có thể được sử dụng để tạo cá thể 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 Dim một lần nữa 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 biến cục bộ, reg, chỉ tồn tại trong bối cảnh đó (phụ). Biến reg với cấp độ mô-đun Scope mà bạn sẽ sử dụng ở mọi nơi khác vẫn là Nothing.

Thiếu toán tử New là nguyên nhân số 1 của NullReference Exceptionsđược thấy trong các câu hỏi về 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ụngNew: Sử dụngNewToán tử tạo một new object và gọiSub New- constructor - 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à Type của nó. Phạm vi của biến - cho dù nó tồn tại cho toàn bộ mô-đun/lớp hoặc là cục bộ của một thủ tục - được xác định bởi trong đó nó được khai báo. Private | Friend | Public xá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>New là 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 đối tượng Foo trong đó thì không.

Biện pháp

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ẽ khiến việc có một phần tử không có đối tượng hợp lệ trở nên 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 - myList chỉ đượ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ì barList chỉ được khai báo, không được khởi tạo. Tạo một thể hiện của Foo cũng sẽ không tạo ra một thể hiện của barList nội bộ. 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, hãy 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 mang lại nhiều cơ hội cho NullReference vì có thể sử dụng nhiều đối tượng (Command, Connection, Transaction, Dataset, DataTable, DataRows....). 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. - khái niệm 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, đối tượng Dataset ds đã được khai báo, nhưng một thể hiện không bao giờ được tạo. DataAdapter sẽ điền vào DataSet hiện tại, không tạo một cái. Trong trường hợp này, vì ds là 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, dường như 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

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 lỗi đánh máy là một vấn đề ở đây: Employees vs Employee. Không có DataTable được đặt tên là "Nhân viên" được tạo, vì vậy kết quả NullReferenceException cố gắng truy cập nó. Một vấn đề tiềm năng khác là giả sử sẽ có Items có thể không như vậy khi SQL bao gồm mệnh đề WHERE.

Biện pháp

Vì bảng này sử dụng một bảng, sử dụng Tables(0) sẽ tránh được lỗi chính tả. Kiểm tra Rows.Count cũ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

Fill là một hàm trả về số lượng Rows bị ả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

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

Biện pháp là giống nhau, tham chiếu bảng theo chỉ mục:

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 kiểm tra Items trong khi cả myFooBar cũ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 dẫn của các đối tượng một lần:

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

AndAlso là quan trọng. Các thử nghiệm tiếp theo sẽ không được thực hiện khi gặp điều kiện False đầu tiên. Điều này cho phép mã 'khoan' an toàn vào (các) đối tượng một 'cấp' tại một thời điểm, chỉ đánh giá myFoo.Bar 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 đối tượng null. Điều này cũng áp dụng cho các điều khiển:

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

Ở đây, myWebBrowser hoặc Document có thể là Không có gì hoặc phần tử formfld1 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.SelectedItem cũng có thể là Nothing, vì vậy ListBox1.SelectedItem.ToString sẽ dẫn đến NRE.

Biện pháp

Xác thực dữ liệu trước khi sử dụng (cũng sử dụng các tham số Option Strict và 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 rằng Controls khô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 hàm tạo tạo Form hoặ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 gán somevar sẽ dẫn đến NRE ngay lập tức vì Không có gì không có thuộc tính .Text

Tham chiếu các phần tử mảng sau này sẽ dẫn đến một NRE. Nếu bạn làm điều này trong Form_Load, do một lỗi kỳ 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 là chi tiết trong bài đăng này . Đối với mục đích của chúng tôi, điều quan trọng là khi xảy ra sự cố thảm khốc trong khi tạo biểu mẫu (sự kiện Sub New hoặc Form Load), các ngoại lệ có thể không được báo cáo, mã 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 New hoặc Form Load của bạn sẽ chạy sau NRE, nên rất nhiều thứ khác có thể không được khởi tạo.

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 những điều này bất hợp pháp ở nơi chúng là:

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

Điều gây tò mò là VB không đưa ra cảnh báo, nhưng biện pháp khắc phục là khai báo các thùng chứa ở cấp biểu mẫu, nhưng khởi tạo chúng trong trình xử lý sự kiện tải biểu mẫu khi các điều khiển do tồn tại. Điều này có thể được thực hiện trong Sub New miễn là mã của bạn nằm sau lệnh gọi InitializeComponent:

' 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ư GroupBox hoặ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, Nothing sẽ lại đượ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 shows you the error of your ways

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

Biện pháp

Thay vì tham chiếu gián tiếp theo tên bằng cách sử dụng bộ sưu tập Controls 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 trong đó 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à có thể dẫn đến NullReferenceException'. Bạn có thể chặn cảnh báo, bằng cách thay thế Exit Function bằ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 someCondition = False sẽ dẫn đến NRE:

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

Biện pháp

Thay thế Exit Function trong hàm bằng Return bList. Trả lại một trốngList không giống như trả về Nothing. Nếu có khả năng một đối tượng được trả về có thể là 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à trường hợp một đối tượng không được tạo như mong đợi, nhưng cũng cho thấy tính hữu dụng của bộ đếm Catch trống.

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

Một khối Catch 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 khối Finally. Trong các tình huống khác, một Catch trống có thể dẫn đến một điều gì đó khác đi sâu hơn về phía trước 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ô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
        ...

Hàm IsDBNull được sử dụng để kiểm tra nếu a value bằng 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

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
       ...

FirstOrDefault trả về mục đầu tiên hoặc giá trị mặc định, đó là Nothing 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 không thể tìm thấy CheckBox với chkName (hoặc tồn tại trong GroupBox), thì chk sẽ là Không có gì và cố gắng tham chiếu bất kỳ thuộc tính nào sẽ dẫn đến ngoại lệ.

Biện pháp

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

Đặ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 DataGridView của bạn có AllowUserToAddRowsTrue (mặc định), Cells trong hàng trống/mới ở phía dưới tất cả sẽ chứa 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

Sử dụng vòng lặp For/Each và kiểm tra thuộc tính IsNewRow để xác định xem đó có phải là hàng cuối cùng không. Điều này hoạt động cho dù AllowUserToAddRows có đú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 vòng lặp For n, hãy sửa đổi số hàng hoặc sử dụng Exit For khi IsNewRow là đú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 mục từ My.SettingsStringCollection có 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 dự kiến ​​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 Nothing khi không có mục nào trong trình chỉnh sửa Cài đặt để thêm.

Biện pháp

Khởi tạo bộ sưu tập cài đặt trong trình xử lý sự kiện Load 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, bộ sưu tập Settings sẽ chỉ cần được khởi tạo khi ứng dụng chạy lần đầu tiên. 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 đó xóa giá trị giả.


Những điểm chính

Có lẽ bạn đã quên toán tử New.

hoặc là

Một cái gì đó bạn giả định sẽ thực hiện hoàn hảo để trả về 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).


Ngoại lệ NullReference MSDN

297
Nat Pongjardenlarp

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

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

Nó sẽ ném NullReferenceException vào dàn diễ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ã mà bạn không sở hữu và ví dụ như đượ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")%>" />

Ở đây, SelectedDate trên thực tế là một thuộc tính - thuộc loại DateTime - thuộc loại Calendar Điều khiển web và ràng buộc hoàn toàn có thể trả về một thứ 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 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 ...

223
Simon Mourier

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", thì 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ó rỗng hay không, hãy kiểm tra nó bằng 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.

154
Chris B. Behrens

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
}
150
Jonathan Wood

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/null cho biến tham chiếu, điều đó có nghĩa là nó không thực sự giữ 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/null theo cách thủ công hoặc bạn đã gọi một hàm đặt biến thành Nothing/null cho bạn.

94
code master

Một ví dụ về trường hợp ngoại lệ này bị ném 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 đối với 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 ném 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êm thông tin trongC # NullReferenceException và Null Parameter.

85
Alex KeySmith

Nếu bạn chưa khởi tạo 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ẽ ném 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 loại giá trịloại tham chiếu .

Vì vậy, nếu bạn đang xử lý loại giá trị, NullReferenceExceptions có thể không xảy ra. Mặc dù bạn cần phải cảnh giác khi giao dịch với 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
81
Fabian Bigler

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

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, BookCar là các loại không tương thích; a Car không thể được chuyển đổi/chuyển thành Book. Khi diễn viên này thất bại, as trả về null. Sử dụng mybook sau khi điều này gây ra NullReferenceException.

Nói chung, bạn nên sử dụng 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 loại này làm loại cụ thể, thì hãy sử dụng as:

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

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 ()

63
user1814380

While what gây ra một NullReferenceExceptions và 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à cách độc lập gỡ lỗi ngoại lệ như vậy trong quá trình phát triển.

Trong Visual Studio, điều này thường dễ dàng nhờ vào Visual Studio Debugger .


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 để tôi cho phép 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, có thể hữu ích khi sử dụng Debugger.Break , sẽ nhắc nhở để khởi chạy trình gỡ lỗi.

Bây giờ, khi ném NullReferenceException (hoặc chưa được 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 lượng 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 các biểu thức đang chạy 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 str1 là null hay nếu str2 là null.

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

Một khi ở đâu ngoại lệ là ném đã được định vị, thường là lý do tầm thường để tìm ra 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 Th 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 giới thiệu Just My Mã mà tôi khuyên bạn cũng nên bật.

Nếu bạn đang gỡ lỗi với Just My Code được bật, 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
user2864740

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

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

trong đó mộtbỏ hộpconvert (cast) từobject (hoặc từ một trong các lớp System.ValueType hoặc System.Enum hoặc từ một loại giao diện) đến một loại giá trị (khác với Nullable<>) tự nó cung cấp NullReferenceException.

Theo hướng khác, aquyền anhconvert từ a Nullable<>HasValue bằng với falseto một loại tham chiếu, có thể cung cấp null tham 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 trường hợp đấm bốc Nullable<>.

55
Jeppe Stig Nielsen

Thêm một 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 ()

40
AbhinavRanjan

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 của 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.

39
John Saunders

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 Container tiêm phụ thuộc có thể được sử dụng để khởi tạo dịch vụ để 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();
    }
}
38
Mukus

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 đang á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 đặt lớp bất biến, và/hoặc thậm chí hàm/phương thức điều kiện tiên quyếtpostconditions trên hệ thống của bạn, trong khi phát triển.

Nói tóm lại, 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 có 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 hàm/phương thức phải tuân theo một số ràng buộc được đặt và never vi phạm chúng và postconditions có nghĩa là đầu ra của hàm/phương thức phải làm theo các ràng buộc thiết lập một lần nữa mà không bao giờ vi phạm chúng. Các điều kiện hợp đồng nên 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ị bị vô hiệu hóa trong bản phát hành, để tối đa hóa hệ thống được phát triển hiệu suất.

Bằng cách này, bạn có thể tránh các trường hợp NullReferenceException là kết quả của việc vi phạm các ràng buộc được đặt. Ví dụ: nếu bạn sử dụng một thuộc tính đối tượng X trong một lớp và sau đó thử gọi một trong các phương thức của nó và X có 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 Mã hợp đồng 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 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 .

37
Nick Louloudakis

Một NullReferenceException được ném khi chúng ta đang cố truy cập 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 
    
34
Hemant Bavle

TL; DR: Thử sử dụng Html.Partial thay vì Renderpage


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

@{
    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 có Html.Partial để bắt đầu là vì Visual Studio đôi khi ném các dòng nguệch ngoạc trông có lỗi trong Html.Partial nếu trong vòng lặp foreach được xây dựng khác, mặc dù đó không thực sự là 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 vòng lặp foreach thành như sau:

@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.

30
Travis Heeter

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 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 ArgumentNullException thậm chí 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 obj sẽ được kiểm tra null khi chạy. Xem: Kiểm tra null PostSharp

Giải pháp mã đơn giản

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 truyền tới 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 đối tượng Person cho một phương thức lấy 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 tính Value. 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 là 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 Tiện ích mở rộng

Kết hợp NotNull<T> với một phương thức 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à null thì toà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 country là một đối tượng thuộc loại Country có một thuộc tính được gọi là State, v.v. Nếu country, State, County hoặc Citynull thì address will benull. Therefore you only have to check whetheraddressisnull`.

Đâ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 tích hợp như Nullable?

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

Sẽ thật tuyệt nếu C # có thứ gì đó giống như cấu trúc NotNull<T> ở 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).

22
Luis Perez

Bạn có thể sửa lỗi NullReferenceException một cách rõ ràng bằng cách sử dụng Toán tử có đ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
M.Hassan

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;  
     }
}
8
Jaimin Dave

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 Edge, 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

Các từ điển chung trong .NET không an toàn cho luồng và chúng đôi khi có thể ném NullReference hoặc thậm chí (thường xuyên hơn) một KeyNotFoundException khi 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 một NullReferenceException bị ném bởi mã unsafe, bạn có thể xem các biến con trỏ của mình và kiểm tra xem chúng có phải là IntPtr.Zero hay 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 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)

8
jazzcat

Tham chiếu NullReferenceException hoặc Object không được đặt thành phiên bản của một đố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
Nick

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 new ở 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ếuGCđã thu thập đối tượng ... Đ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 đối tượng sau đó có thể xảy ra rằng GC có thể đã dọn 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");

2
Akash Gutha

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 theo một số cách:

  1. Thêm một RigidBody vào đối tượng của bạn với 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 dưới các hàm Start() hoặc Awake() 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).
[.__.] Tận hưởng và tận hưởng những trò chơi thú vị!

0
CausticLasagne

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.

Vd

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
0
Hiran