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?
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 ý:
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.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:
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
.
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:
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ủaNullReference 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ụng
New
: Sử dụngNew
Toá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 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>
và 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:
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 .
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:
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 .
If myFoo.Bar.Items IsNot Nothing Then
...
Mã chỉ đang kiểm tra Items
trong khi cả myFoo
và Bar
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.
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...
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à:
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:
"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...})
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 đượ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.
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...
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 ...
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 dgvBooks
có AutoGenerateColumns = 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
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ó AllowUserToAddRows
là True
(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.
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.Settings
là StringCollection
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ả.
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).
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 ...
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:
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.
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
}
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ànhNothing
/null
cho bạn.
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.
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ị và 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) :
Các loại giá trị (bạn chỉ có thể bỏ qua các loại này) :
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, Book
và Car
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) {
// ...
}
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 ()
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
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<>
có HasValue
bằng với false
to 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<>
.
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 ()
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.
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();
}
}
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ết và postconditions 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 .
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ụ:
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
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
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.
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 City
là null
thì address will be
null. Therefore you only have to check whether
addressis
null`.
Đâ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)
.
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
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;
}
}
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:
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.
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)
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.
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:
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.
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
}
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");
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:
rb = GetComponent<Rigidbody>();
Start()
hoặc Awake()
của bạn.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ị!
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