Tôi có một số mã và khi nó thực thi, nó ném một NullReferenceException
, nói:
Tham chiếu đối tượng không được đặt thành một thể hiện của một đối tượng.
Điều này có nghĩa là gì và tôi có thể làm gì để khắc phục lỗi này?
Tôi có một số mã và khi nó thực thi, nó ném một NullReferenceException
, nói:
Tham chiếu đối tượng không được đặt thành một thể hiện của một đối tượng.
Điều này có nghĩa là gì và tôi có thể làm gì để khắc phục lỗi này?
Câu trả lời:
Bạn đang cố gắng sử dụng một cái gì đó null
(hoặc Nothing
trong VB.NET). Điều này có nghĩa là bạn hoặc đặt nó thành null
, hoặc bạn không bao giờ đặt nó thành bất cứ điều gì cả.
Giống như bất cứ điều gì khác, null
được thông qua xung quanh. Nếu nó nằm null
trong phương thức "A", thì đó có thể là phương thức "B" đã truyền a null
cho phương thức "A".
null
có thể có ý nghĩa khác nhau:
NullReferenceException
.null
chủ ý để chỉ ra rằng không có giá trị có ý nghĩa. Lưu ý rằng C # có khái niệm về kiểu dữ liệu nullable cho các biến (như bảng cơ sở dữ liệu có thể có các trường nullable) - bạn có thể gán null
cho chúng để chỉ ra rằng không có giá trị nào được lưu trữ trong đó, ví dụ như int? a = null;
dấu hỏi cho biết nó được phép lưu trữ null biến a
. Bạn có thể kiểm tra xem có if (a.HasValue) {...}
hoặc với if (a==null) {...}
. Các biến không thể, như a
ví dụ này, cho phép truy cập giá trị a.Value
một cách rõ ràng hoặc chỉ như bình thường thông qua a
. a.Value
ném một InvalidOperationException
thay vì một NullReferenceException
nếu a
lànull
- bạn nên thực hiện kiểm tra trước, tức là nếu bạn có một biến không thể khác int b;
thì bạn nên thực hiện các bài tập như if (a.HasValue) { b = a.Value; }
hoặc ngắn hơn if (a != null) { b = a; }
.Phần còn lại của bài viết này đi sâu vào chi tiết hơn và cho thấy những sai lầm mà nhiều lập trình viên thường mắc phải có thể dẫn đến a NullReferenceException
.
Việc runtime
ném NullReferenceException
luôn luôn có nghĩa tương tự: bạn đang cố gắng sử dụng một tham chiếu và tham chiếu không được khởi tạo (hoặc nó đã được khởi tạo một lần , nhưng không còn được khởi tạo nữa ).
Điều này có nghĩa là tham chiếu là null
và bạn không thể truy cập các thành viên (như phương thức) thông qua null
tham chiếu. Trường hợp đơn giản nhất:
string foo = null;
foo.ToUpper();
Điều này sẽ ném một NullReferenceException
dòng thứ hai vì bạn không thể gọi phương thức cá thể ToUpper()
trên một string
tham chiếu trỏ đến null
.
Làm thế nào để bạn tìm thấy nguồn của một NullReferenceException
? Ngoài việc nhìn vào ngoại lệ, nó sẽ được ném chính xác tại vị trí xảy ra, các quy tắc gỡ lỗi chung trong Visual Studio áp dụng: đặt các điểm dừng chiến lược và kiểm tra các biến của bạn , bằng cách di chuột qua tên của chúng, mở một ( Nhanh) Xem cửa sổ hoặc sử dụng các bảng gỡ lỗi khác nhau như Địa phương và Ô tô.
Nếu bạn muốn tìm nơi tham chiếu được đặt hoặc không được đặt, nhấp chuột phải vào tên của nó và chọn "Tìm tất cả tài liệu tham khảo". Sau đó, bạn có thể đặt một điểm dừng ở mọi vị trí tìm thấy và chạy chương trình của bạn với trình gỡ lỗi được đính kèm. Mỗi khi trình gỡ lỗi phá vỡ điểm dừng như vậy, bạn cần xác định xem bạn có mong muốn tham chiếu là không null hay không, kiểm tra biến và xác minh rằng nó trỏ đến một thể hiện khi bạn mong đợi.
Bằng cách theo dõi chương trình theo cách này, bạn có thể tìm thấy vị trí mà cá thể không nên rỗng và tại sao nó không được đặt đúng.
Một số tình huống phổ biến trong đó ngoại lệ có thể được ném:
ref1.ref2.ref3.member
Nếu ref1 hoặc ref2 hoặc ref3 là null, thì bạn sẽ nhận được a NullReferenceException
. Nếu bạn muốn giải quyết vấn đề, sau đó tìm ra cái nào là null bằng cách viết lại biểu thức thành tương đương đơn giản hơn:
var r1 = ref1;
var r2 = r1.ref2;
var r3 = r2.ref3;
r3.member
Cụ thể, trong HttpContext.Current.User.Identity.Name
, HttpContext.Current
có thể là null hoặc User
tài sản có thể là null hoặc Identity
tài sản có thể là null.
public class Person
{
public int Age { get; set; }
}
public class Book
{
public Person Author { get; set; }
}
public class Example
{
public void Foo()
{
Book b1 = new Book();
int authorAge = b1.Author.Age; // You never initialized the Author property.
// there is no Person to get an Age from.
}
}
Nếu bạn muốn tránh tham chiếu null (Person) null, bạn có thể khởi tạo nó trong hàm tạo của đối tượng cha (Book).
Điều tương tự cũng áp dụng cho các trình khởi tạo đối tượng lồng nhau:
Book b1 = new Book
{
Author = { Age = 45 }
};
Điều này dịch sang
Book b1 = new Book();
b1.Author.Age = 45;
Trong khi new
từ khóa được sử dụng, nó chỉ tạo ra một thể hiện mới Book
, nhưng không phải là một thể hiện mới Person
, vì vậy Author
thuộc tính vẫn còn null
.
public class Person
{
public ICollection<Book> Books { get; set; }
}
public class Book
{
public string Title { get; set; }
}
Bộ sưu tập lồng nhau Initializers
hoạt động giống nhau:
Person p1 = new Person
{
Books = {
new Book { Title = "Title1" },
new Book { Title = "Title2" },
}
};
Điều này dịch sang
Person p1 = new Person();
p1.Books.Add(new Book { Title = "Title1" });
p1.Books.Add(new Book { Title = "Title2" });
Các new Person
chỉ tạo ra một thể hiện của Person
, nhưng Books
bộ sưu tập vẫn là null
. Initializer
Cú pháp bộ sưu tập không tạo ra một bộ sưu tập p1.Books
, nó chỉ dịch sang các p1.Books.Add(...)
câu lệnh.
int[] numbers = null;
int n = numbers[0]; // numbers is null. There is no array to index.
Person[] people = new Person[5];
people[0].Age = 20 // people[0] is null. The array was allocated but not
// initialized. There is no Person to set the Age for.
long[][] array = new long[1][];
array[0][0] = 3; // is null because only the first dimension is yet initialized.
// Use array[0] = new long[2]; first.
Dictionary<string, int> agesForNames = null;
int age = agesForNames["Bob"]; // agesForNames is null.
// There is no Dictionary to perform the lookup.
public class Person
{
public string Name { get; set; }
}
var people = new List<Person>();
people.Add(null);
var names = from p in people select p.Name;
string firstName = names.First(); // Exception is thrown here, but actually occurs
// on the line above. "p" is null because the
// first element we added to the list is null.
public class Demo
{
public event EventHandler StateChanged;
protected virtual void OnStateChanged(EventArgs e)
{
StateChanged(this, e); // Exception is thrown here
// if no event handlers have been attached
// to StateChanged event
}
}
###Bad Naming Conventions:
If you named fields differently from locals, you might have realized that you never initialized the field.
lớp công khai Form1 {khách hàng riêng của khách hàng;
private void Form1_Load(object sender, EventArgs e)
{
Customer customer = new Customer();
customer.Name = "John";
}
private void Button_Click(object sender, EventArgs e)
{
MessageBox.Show(customer.Name);
}
}
Điều này có thể được giải quyết bằng cách tuân theo quy ước đối với các trường tiền tố có dấu gạch dưới:
private Customer _customer;
public partial class Issues_Edit : System.Web.UI.Page
{
protected TestIssue myIssue;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// Only called on first load, not when button clicked
myIssue = new TestIssue();
}
}
protected void SaveButton_Click(object sender, EventArgs e)
{
myIssue.Entry = "NullReferenceException here!";
}
}
// if the "FirstName" session value has not yet been set,
// then this line will throw a NullReferenceException
string firstName = Session["FirstName"].ToString();
Nếu ngoại lệ xảy ra khi tham chiếu một thuộc tính của @Model
một ASP.NET MVC View
, bạn cần hiểu rằng Model
được đặt trong phương thức hành động của bạn, khi bạn return
xem. Khi bạn trả về một mô hình trống (hoặc thuộc tính mô hình) từ bộ điều khiển của bạn, ngoại lệ xảy ra khi các khung nhìn truy cập vào nó:
// Controller
public class Restaurant:Controller
{
public ActionResult Search()
{
return View(); // Forgot the provide a Model here.
}
}
// Razor view
@foreach (var restaurantSearch in Model.RestaurantSearch) // Throws.
{
}
<p>@Model.somePropertyName</p> <!-- Also throws -->
WPF
điều khiển được tạo trong khi gọi InitializeComponent
theo thứ tự chúng xuất hiện trong cây trực quan. A NullReferenceException
sẽ được nêu ra trong trường hợp các điều khiển được tạo sớm với các trình xử lý sự kiện, v.v., sẽ kích hoạt trong InitializeComponent
đó tham chiếu các điều khiển được tạo muộn.
Ví dụ :
<Grid>
<!-- Combobox declared first -->
<ComboBox Name="comboBox1"
Margin="10"
SelectedIndex="0"
SelectionChanged="comboBox1_SelectionChanged">
<ComboBoxItem Content="Item 1" />
<ComboBoxItem Content="Item 2" />
<ComboBoxItem Content="Item 3" />
</ComboBox>
<!-- Label declared later -->
<Label Name="label1"
Content="Label"
Margin="10" />
</Grid>
Ở đây comboBox1
được tạo ra trước label1
. Nếu comboBox1_SelectionChanged
cố gắng tham chiếu `nhãn1, nó sẽ chưa được tạo.
private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
label1.Content = comboBox1.SelectedIndex.ToString(); // NullReference here!!
}
Thay đổi thứ tự của các khai báo trong XAML
(nghĩa là liệt kê label1
trước comboBox1
, bỏ qua các vấn đề về triết lý thiết kế, ít nhất sẽ giải quyết vấn đề NullReferenceException
ở đây.
as
var myThing = someObject as Thing;
Điều này không ném InvalidCastException
nhưng trả về một null
khi diễn viên thất bại (và khi someObject
chính nó là null). Vì vậy, hãy nhận thức về điều đó.
FirstOrDefault()
vàSingleOrDefault()
Các phiên bản đơn giản First()
và Single()
ném ngoại lệ khi không có gì. Các phiên bản "OrDefault" trả về null trong trường hợp đó. Vì vậy, hãy nhận thức về điều đó.
foreach
ném khi bạn cố gắng lặp bộ sưu tập null. Thường gây ra bởi null
kết quả bất ngờ từ các phương thức trả về bộ sưu tập.
List<int> list = null;
foreach(var v in list) { } // exception
Ví dụ thực tế hơn - chọn các nút từ tài liệu XML. Sẽ ném nếu các nút không được tìm thấy nhưng gỡ lỗi ban đầu cho thấy tất cả các thuộc tính hợp lệ:
foreach (var node in myData.MyXml.DocumentNode.SelectNodes("//Data"))
null
và bỏ qua các giá trị null.Nếu bạn cho rằng tham chiếu đôi khi là null, bạn có thể kiểm tra tham chiếu đó null
trước khi truy cập các thành viên thể hiện:
void PrintName(Person p)
{
if (p != null)
{
Console.WriteLine(p.Name);
}
}
null
và cung cấp một giá trị mặc định.Các phương thức gọi mà bạn mong đợi trả về một thể hiện có thể trả về null
, ví dụ khi không thể tìm thấy đối tượng đang tìm kiếm. Bạn có thể chọn trả về giá trị mặc định khi gặp trường hợp này:
string GetCategory(Book b)
{
if (b == null)
return "Unknown";
return b.Category;
}
null
từ các cuộc gọi phương thức và ném một ngoại lệ tùy chỉnh.Bạn cũng có thể ném một ngoại lệ tùy chỉnh, chỉ để bắt nó trong mã cuộc gọi:
string GetCategory(string bookTitle)
{
var book = library.FindBook(bookTitle); // This may return null
if (book == null)
throw new BookNotFoundException(bookTitle); // Your custom exception
return book.Category;
}
Debug.Assert
nếu một giá trị không bao giờ nên null
, để nắm bắt vấn đề sớm hơn ngoại lệ xảy ra.Khi bạn biết trong quá trình phát triển, một phương thức có thể có thể, nhưng không bao giờ nên quay lại null
, bạn có thể sử dụng Debug.Assert()
để phá vỡ càng sớm càng tốt khi nó xảy ra:
string GetTitle(int knownBookID)
{
// You know this should never return null.
var book = library.GetBook(knownBookID);
// Exception will occur on the next line instead of at the end of this method.
Debug.Assert(book != null, "Library didn't return a book for known book ID.");
// Some other code
return book.Title; // Will never throw NullReferenceException in Debug mode.
}
Mặc dù kiểm tra này sẽ không kết thúc trong bản phát hành của bạn , khiến nó bị ném NullReferenceException
lại khi book == null
ở chế độ chạy trong chế độ phát hành.
GetValueOrDefault()
cho nullable
các loại giá trị để cung cấp một giá trị mặc định khi chúng là null
.DateTime? appointment = null;
Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));
// Will display the default value provided (DateTime.Now), because appointment is null.
appointment = new DateTime(2022, 10, 20);
Console.WriteLine(appointment.GetValueOrDefault(DateTime.Now));
// Will display the appointment date, not the default
??
[C #] hoặc If()
[VB].Tốc ký để cung cấp một giá trị mặc định khi null
gặp phải:
IService CreateService(ILogger log, Int32? frobPowerLevel)
{
var serviceImpl = new MyService(log ?? NullLog.Instance);
// Note that the above "GetValueOrDefault()" can also be rewritten to use
// the coalesce operator:
serviceImpl.FrobPowerLevel = frobPowerLevel ?? 5;
}
?.
hoặc ?[x]
cho các mảng (có sẵn trong C # 6 và VB.NET 14):Điều này đôi khi cũng được gọi là toán tử điều hướng an toàn hoặc toán tử Elvis (sau hình dạng của nó). Nếu biểu thức ở phía bên trái của toán tử là null, thì phía bên phải sẽ không được ước tính và thay vào đó null được trả về. Điều đó có nghĩa là những trường hợp như thế này:
var title = person.Title.ToUpper();
Nếu người đó không có tiêu đề, điều này sẽ đưa ra một ngoại lệ vì họ đang cố gắng gọi ToUpper
một tài sản có giá trị null.
Trong C# 5
và dưới đây, điều này có thể được bảo vệ với:
var title = person.Title == null ? null : person.Title.ToUpper();
Bây giờ biến tiêu đề sẽ là null thay vì ném một ngoại lệ. C # 6 giới thiệu một cú pháp ngắn hơn cho việc này:
var title = person.Title?.ToUpper();
Điều này sẽ dẫn đến biến tiêu đề đang tồn tại null
và cuộc gọi đến ToUpper
không được thực hiện nếu person.Title
có null
.
Tất nhiên, bạn vẫn phải kiểm tra title
null hoặc sử dụng toán tử điều kiện null cùng với toán tử hợp nhất null ( ??
) để cung cấp giá trị mặc định:
// regular null check
int titleLength = 0;
if (title != null)
titleLength = title.Length; // If title is null, this would throw NullReferenceException
// combining the `?` and the `??` operator
int titleLength = title?.Length ?? 0;
Tương tự như vậy, đối với các mảng bạn có thể sử dụng ?[i]
như sau:
int[] myIntArray=null;
var i=5;
int? elem = myIntArray?[i];
if (!elem.HasValue) Console.WriteLine("No value");
Điều này sẽ làm như sau: Nếu myIntArray
là null, biểu thức trả về null và bạn có thể kiểm tra nó một cách an toàn. Nếu nó chứa một mảng, nó sẽ làm tương tự như:
elem = myIntArray[i];
và trả về i<sup>th</sup>
phần tử.
Được giới thiệu trong C# 8
đó các kiểu tham chiếu null và null null thực hiện phân tích tĩnh trên các biến và cung cấp cảnh báo trình biên dịch nếu một giá trị có thể có khả năng null hoặc đã được đặt thành null. Các kiểu tham chiếu nullable cho phép các kiểu được phép rõ ràng là null.
Bối cảnh chú thích nullable và bối cảnh cảnh báo nullable có thể được đặt cho một dự án bằng cách sử dụng Nullable
phần tử trong csproj
tệp của bạn . Phần tử này cấu hình cách trình biên dịch diễn giải tính không hợp lệ của các loại và những cảnh báo nào được tạo. Cài đặt hợp lệ là:
Một loại tham chiếu nullable được ghi chú bằng cách sử dụng cú pháp giống như các loại giá trị nullable: a ?
được gắn vào loại biến.
C#
hỗ trợ "khối lặp" (được gọi là "trình tạo" trong một số ngôn ngữ phổ biến khác). Các trường hợp ngoại lệ của Null có thể đặc biệt khó khăn để gỡ lỗi trong các khối lặp vì thực thi bị trì hoãn:
public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
{
for (int i = 0; i < count; ++i)
yield return f.MakeFrob();
}
...
FrobFactory factory = whatever;
IEnumerable<Frobs> frobs = GetFrobs();
...
foreach(Frob frob in frobs) { ... }
Nếu whatever
kết quả trong null
đó MakeFrob
sẽ ném. Bây giờ, bạn có thể nghĩ rằng điều đúng đắn cần làm là:
// DON'T DO THIS
public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
{
if (f == null)
throw new ArgumentNullException("f", "factory must not be null");
for (int i = 0; i < count; ++i)
yield return f.MakeFrob();
}
Tại sao điều này là sai? Bởi vì khối lặp không thực sự chạy cho đến khi foreach
! Cuộc gọi GetFrobs
đơn giản trả về một đối tượng mà khi lặp sẽ chạy khối lặp.
Bằng cách viết một kiểm tra null như thế này, bạn ngăn chặn sự vô hiệu hóa null, nhưng bạn di chuyển ngoại lệ đối số null đến điểm lặp , không đến điểm của cuộc gọi và điều đó rất khó hiểu để gỡ lỗi .
Cách khắc phục chính xác là:
// DO THIS
public IEnumerable<Frob> GetFrobs(FrobFactory f, int count)
{
// No yields in a public method that throws!
if (f == null)
throw new ArgumentNullException("f", "factory must not be null");
return GetFrobsForReal(f, count);
}
private IEnumerable<Frob> GetFrobsForReal(FrobFactory f, int count)
{
// Yields in a private method
Debug.Assert(f != null);
for (int i = 0; i < count; ++i)
yield return f.MakeFrob();
}
Đó là, tạo một phương thức trợ giúp riêng có logic khối iterator và một phương thức bề mặt công khai thực hiện kiểm tra null và trả về iterator. Bây giờ khi GetFrobs
được gọi, kiểm tra null xảy ra ngay lập tức và sau đó GetFrobsForReal
thực thi khi chuỗi được lặp lại.
Nếu bạn kiểm tra nguồn tham chiếu cho LINQ
các Đối tượng, bạn sẽ thấy kỹ thuật này được sử dụng xuyên suốt. Nó hơi khó viết hơn một chút, nhưng nó làm cho việc sửa lỗi vô hiệu hóa dễ dàng hơn nhiều. Tối ưu hóa mã của bạn để thuận tiện cho người gọi, không phải là sự thuận tiện của tác giả .
C#
có chế độ "không an toàn", đúng như tên gọi của nó, cực kỳ nguy hiểm vì các cơ chế an toàn thông thường cung cấp an toàn bộ nhớ và an toàn loại không được thi hành. Bạn không nên viết mã không an toàn trừ khi bạn hiểu rõ và sâu sắc về cách thức hoạt động của bộ nhớ .
Trong chế độ không an toàn, bạn cần lưu ý đến hai sự thật quan trọng:
Để hiểu lý do tại sao, điều đó giúp hiểu cách .NET tạo ra các ngoại lệ vô hiệu hóa null ngay từ đầu. (Các chi tiết này áp dụng cho .NET chạy trên Windows; các hệ điều hành khác sử dụng các cơ chế tương tự.)
Bộ nhớ được ảo hóa trong Windows
; mỗi tiến trình có một không gian bộ nhớ ảo gồm nhiều "trang" bộ nhớ được hệ điều hành theo dõi. Mỗi trang của bộ nhớ có các cờ được đặt trên đó để xác định cách sử dụng: đọc từ, ghi vào, thực thi, v.v. Các thấp nhất trang được đánh dấu là "tạo ra một lỗi nếu từng sử dụng trong bất kỳ cách nào".
Cả một con trỏ null và một tham chiếu null trong C#
đều được biểu diễn bên trong dưới dạng số 0 và do đó, bất kỳ nỗ lực nào để đưa nó vào bộ nhớ tương ứng của nó đều khiến hệ điều hành gây ra lỗi. Thời gian chạy .NET sau đó phát hiện lỗi này và biến nó thành ngoại lệ vô hiệu hóa null.
Đó là lý do tại sao hội nghị cả con trỏ null và tham chiếu null tạo ra cùng một ngoại lệ.
Còn điểm thứ hai thì sao? Việc hủy bỏ bất kỳ con trỏ không hợp lệ nào rơi vào trang thấp nhất của bộ nhớ ảo đều gây ra lỗi hệ điều hành tương tự và do đó cũng có ngoại lệ tương tự.
Tại sao điều này có ý nghĩa? Chà, giả sử chúng ta có một cấu trúc chứa hai số nguyên và một con trỏ không được quản lý bằng null. Nếu chúng tôi cố gắng hủy đăng ký int thứ hai trong cấu trúc, thì CLR
sẽ không cố gắng truy cập vào bộ lưu trữ ở vị trí 0; nó sẽ truy cập vào bộ lưu trữ tại vị trí bốn. Nhưng về mặt logic, đây là một sự vô hiệu hóa bởi vì chúng tôi đang đến địa chỉ đó thông qua null.
Nếu bạn đang làm việc với mã không an toàn và bạn nhận được ngoại lệ vô hiệu hóa null, chỉ cần lưu ý rằng con trỏ vi phạm không cần phải là null. Nó có thể là bất kỳ vị trí nào trong trang thấp nhất và ngoại lệ này sẽ được tạo ra.
new Book { Author = { Age = 45 } };
Làm thế nào để khởi tạo bên trong thậm chí ... Tôi không thể nghĩ đến một tình huống mà init bên trong sẽ hoạt động, nhưng nó sẽ biên dịch và intellisense hoạt động ... Trừ khi cho các cấu trúc?
Các NullReference Exception
cho Visual Basic là không có khác nhau từ một trong C # . Rốt cuộc, cả hai đều báo cáo cùng một ngoại lệ được xác định trong .NET Framework mà cả hai đều sử dụng. Nguyên nhân duy nhất đối với Visual Basic là rất hiếm (có lẽ chỉ có một).
Câu trả lời này sẽ sử dụng các thuật ngữ, cú pháp và ngữ cảnh của Visual Basic. Các ví dụ được sử dụng đến từ một số lượng lớn các câu hỏi Stack Overflow trước đây. Điều này là để tối đa hóa sự liên quan bằng cách sử dụng các loại tình huống thường thấy trong các bài đăng. Một chút giải thích cũng được cung cấp cho những người có thể cần nó. Một ví dụ tương tự như của bạn rất có thể được liệt kê ở đây.
Ghi chú:
NullReferenceException
(NRE), cách tìm, cách khắc phục và cách tránh. Một NRE có thể được gây ra theo nhiều cách vì vậy đây không chắc là cuộc gặp gỡ duy nhất của bạn.Thông báo "Đối tượng không được đặt thành phiên bản của Đối tượng" có nghĩa là bạn đang cố gắng sử dụng một đối tượng chưa được khởi tạo. Điều này làm sôi một trong những điều sau:
Vì vấn đề là một tham chiếu đối tượng Nothing
, nên câu trả lời là kiểm tra chúng để tìm ra cái nào. Sau đó xác định lý do tại sao nó không được khởi tạo. Giữ chuột trên các biến khác nhau và Visual Studio (VS) sẽ hiển thị giá trị của chúng - thủ phạm sẽ là Nothing
.
Bạn cũng nên xóa bất kỳ khối Thử / Bắt nào khỏi mã có liên quan, đặc biệt là các khối không có gì trong khối Bắt. Điều này sẽ khiến mã của bạn bị sập khi nó cố sử dụng một đối tượng Nothing
. Đây là những gì bạn muốn bởi vì nó sẽ xác định vị trí chính xác của vấn đề và cho phép bạn xác định đối tượng gây ra nó.
Một MsgBox
trong Catch mà hiển thị Error while...
sẽ giúp đỡ rất ít. Phương pháp này cũng dẫn đến các câu hỏi Stack Overflow rất tệ , bởi vì bạn không thể mô tả ngoại lệ thực tế, đối tượng liên quan hoặc thậm chí dòng mã nơi nó xảy ra.
Bạn cũng có thể sử dụng Locals Window
( Gỡ lỗi -> Windows -> Địa phương ) để kiểm tra các đối tượng của bạn.
Một khi bạn biết vấn đề là gì và ở đâu, nó thường khá dễ sửa và nhanh hơn là đăng một câu hỏi mới.
Xem thêm:
Dim reg As CashRegister
...
TextBox1.Text = reg.Amount ' NRE
Vấn đề là Dim
không tạo ra đối tượng CashRegister ; nó chỉ khai báo một biến có tên reg
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 khắc phục
Các New
nhà điều hành thường có thể được sử dụng để tạo ra các ví dụ khi bạn khai báo nó:
Dim reg As New CashRegister ' [New] creates instance, invokes the constructor
' Longer, more explicit form:
Dim reg As CashRegister = New CashRegister
Khi nó chỉ thích hợp để tạo cá thể sau:
Private reg As CashRegister ' Declare
...
reg = New CashRegister() ' Create instance
Lưu ý: Không sử dụng Dim
lại trong một quy trình, bao gồm cả hàm tạo ( Sub New
):
Private reg As CashRegister
'...
Public Sub New()
'...
Dim reg As New CashRegister
End Sub
Điều này sẽ tạo ra một địa phương biến, reg
mà chỉ tồn tại trong bối cảnh đó (phụ). Các reg
biến với cấp module Scope
mà bạn sẽ sử dụng cốt khắp mọi nơi khác Nothing
.
Thiếu
New
toán tử là nguyên nhân số 1NullReference Exceptions
được thấy trong các câu hỏi Stack Overflow được xem xét.Visual Basic cố gắng làm cho quá trình rõ ràng lặp đi lặp lại bằng cách sử dụng
New
: Sử dụngNew
Toán tử tạo một đối tượng mới và gọiSub New
- hàm tạo - nơi đối tượng của bạn có thể thực hiện bất kỳ khởi tạo nào khác.
Để rõ ràng, Dim
(hoặc Private
) chỉ khai báo một biến và của nó Type
. Các Phạm vi của biến - cho dù nó tồn tại cho module toàn bộ / lớp hoặc là địa phương để một thủ tục - được xác định bởi nơi nó được khai báo. Private | Friend | 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 Foo
đối tượng trong đó thì không.
Biện pháp khắc phục
For i As Integer = 0 To arrFoo.Count - 1
arrFoo(i) = New Foo() ' Create Foo instance
arrFoo(i).Bar = i * 10
Next
Việc sử dụng List(Of T)
sẽ làm cho việc có một phần tử không có đối tượng hợp lệ là khá khó khăn:
Dim FooList As New List(Of Foo) ' List created, but it is empty
Dim f As Foo ' Temporary variable for the loop
For i As Integer = 0 To 5
f = New Foo() ' Foo instance created
f.Bar = i * 10
FooList.Add(f) ' Foo object added to list
Next
Để biết thêm thông tin, xem:
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 nội bộ barList
. Nó có thể có ý định làm điều này trong hàm tạo:
Public Sub New ' Constructor
' Stuff to do when a new Foo is created...
barList = New List(Of Bar)
End Sub
Như trước đây, điều này không chính xác:
Public Sub New()
' Creates another barList local to this procedure
Dim barList As New List(Of Bar)
End Sub
Để biết thêm thông tin, xem List(Of T)
Lớp .
Làm việc với cơ sở dữ liệu trình bày nhiều cơ hội cho một NullReference bởi vì có thể có nhiều đối tượng ( Command
, Connection
, Transaction
, Dataset
, DataTable
, DataRows
....) sử dụng cùng một lúc. Lưu ý: Không quan trọng bạn đang sử dụng nhà cung cấp dữ liệu nào - MySQL, SQL Server, OleDB, v.v. - các khái niệm đều giống nhau.
ví dụ 1
Dim da As OleDbDataAdapter
Dim ds As DataSet
Dim MaxRows As Integer
con.Open()
Dim sql = "SELECT * FROM tblfoobar_List"
da = New OleDbDataAdapter(sql, con)
da.Fill(ds, "foobar")
con.Close()
MaxRows = ds.Tables("foobar").Rows.Count ' Error
Như trước đây, ds
đối tượng Dataset đã được khai báo, nhưng một thể hiện không bao giờ được tạo. Các DataAdapter
sẽ điền vào một hiện DataSet
, không tạo ra một. Trong trường hợp này, do 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, như có vẻ là trường hợp với con
, trình biên dịch không thể biết liệu đối tượng có được tạo bởi một thủ tục ngược dòng hay không. Đừng bỏ qua các cảnh báo.
Biện pháp khắc phục
Dim ds As New DataSet
Ví dụ 2
ds = New DataSet
da = New OleDBDataAdapter(sql, con)
da.Fill(ds, "Employees")
txtID.Text = ds.Tables("Employee").Rows(0).Item(1)
txtID.Name = ds.Tables("Employee").Rows(0).Item(2)
Một typo là một vấn đề ở đây: Employees
vs Employee
. Không có DataTable
tên "Nhân viên" được tạo ra, vì vậy NullReferenceException
kết quả cố gắng truy cập nó. Một vấn đề tiềm năng khác là giả sử sẽ có Items
điều đó có thể không xảy ra khi SQL bao gồm mệnh đề WHERE.
Biện pháp khắc phục
Vì điều này sử dụng một bảng, sử dụng Tables(0)
sẽ tránh lỗi chính tả. Kiểm tra Rows.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
ảnh hưởng cũng có thể được kiểm tra:
If da.Fill(ds, "Employees") > 0 Then...
Ví dụ 3
Dim da As New OleDb.OleDbDataAdapter("SELECT TICKET.TICKET_NO,
TICKET.CUSTOMER_ID, ... FROM TICKET_RESERVATION AS TICKET INNER JOIN
FLIGHT_DETAILS AS FLIGHT ... WHERE [TICKET.TICKET_NO]= ...", con)
Dim ds As New DataSet
da.Fill(ds)
If ds.Tables("TICKET_RESERVATION").Rows.Count > 0 Then
Các DataAdapter
sẽ cung cấp TableNames
như trong ví dụ trước, nhưng nó không tên phân tích cú pháp từ SQL hoặc cơ sở dữ liệu bảng. Kết quả là, ds.Tables("TICKET_RESERVATION")
tham chiếu một bảng không tồn tại.
Các Biện pháp khắc phục là như nhau, tham khảo bảng bằng chỉ số:
If ds.Tables(0).Rows.Count > 0 Then
Xem thêm Lớp DataTable .
If myFoo.Bar.Items IsNot Nothing Then
...
Mã chỉ đang thử nghiệm Items
trong khi cả hai 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 đi của các đối tượng cùng một lúc:
If (myFoo IsNot Nothing) AndAlso
(myFoo.Bar IsNot Nothing) AndAlso
(myFoo.Bar.Items IsNot Nothing) Then
....
AndAlso
là quan trọng Các xét nghiệm sau đó sẽ không được thực hiện khi False
gặp điều kiện đầu tiên . Điều này cho phép mã 'khoan' một cách an toàn vào (các) đối tượng một cấp 'tại một thời điểm, myFoo.Bar
chỉ đánh giá sau khi (và nếu) myFoo
được xác định là hợp lệ. Chuỗi đối tượng hoặc đường dẫn có thể khá dài khi mã hóa các đối tượng phức tạp:
myBase.myNodes(3).Layer.SubLayer.Foo.Files.Add("somefilename")
Không thể tham chiếu bất cứ điều gì 'xuôi dòng' của một null
đối tượng. Điều này cũng áp dụng cho các điều khiển:
myWebBrowser.Document.GetElementById("formfld1").InnerText = "some value"
Ở đây, myWebBrowser
hoặc Document
có thể là Không có gì hoặc formfld1
phần tử 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ể Nothing
, do đó ListBox1.SelectedItem.ToString
sẽ dẫn đến một NRE.
Biện pháp khắc phục
Xác thực dữ liệu trước khi sử dụng (cũng sử dụng Option Strict
và tham số SQL):
Dim expiry As DateTime ' for text date validation
If (ComboBox5.SelectedItems.Count > 0) AndAlso
(ListBox1.SelectedItems.Count > 0) AndAlso
(ComboBox2.SelectedItems.Count > 0) AndAlso
(DateTime.TryParse(expiry.Text, expiry) Then
'... do stuff
Else
MessageBox.Show(...error message...)
End If
Ngoài ra, bạn có thể sử dụng (ComboBox5.SelectedItem IsNot Nothing) AndAlso...
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 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 khi hàm tạo tạo Form
hoặc Controls
. Kết quả là:
somevar
chuyển nhượng sẽ dẫn đến một NRE ngay lập tức vì Không có gì không có .Text
tài sảnTham chiếu các phần tử mảng sau này sẽ dẫn đến một NRE. Nếu bạn thực hiện việc này Form_Load
, do một lỗi lạ, IDE có thể không báo cáo ngoại lệ khi nó xảy ra. Ngoại lệ sẽ bật lên sau khi mã của bạn cố gắng sử dụng mảng. "Ngoại lệ im lặng" này được trình bày chi tiết trong bài viết này . Đối với mục đích của chúng tôi, điều quan trọng là khi một điều gì đó thảm khốc xảy ra trong khi tạo biểu mẫu ( Sub New
hoặc Form Load
sự kiện), các ngoại lệ có thể không được đưa ra, mã sẽ thoát khỏi quy trình và chỉ hiển thị biểu mẫu.
Vì không có mã nào khác trong sự kiện Sub New
hoặc Form Load
sự kiện của bạn sẽ chạy sau NRE, rất nhiều thứ khác có thể bị bỏ lại.
Sub Form_Load(..._
'...
Dim name As String = NameBoxes(2).Text ' NRE
' ...
' More code (which will likely not be executed)
' ...
End Sub
Lưu ý điều này áp dụng cho bất kỳ và tất cả các tham chiếu kiểm soát và thành phần làm cho chúng bất hợp pháp ở nơi chúng:
Public Class Form1
Private myFiles() As String = Me.OpenFileDialog1.FileName & ...
Private dbcon As String = OpenFileDialog1.FileName & ";Jet Oledb..."
Private studentName As String = TextBox13.Text
Biện pháp khắc phục một phần
Đó là tò mò mà VB không cung cấp một cảnh báo, nhưng biện pháp khắc phục là để tuyên bố các container ở cấp hình thức, nhưng khởi tạo chúng trong xử lý sự kiện dưới hình thức tải khi các điều khiển làm tồn tại. Điều này có thể được thực hiện Sub New
miễn là mã của bạn sau InitializeComponent
cuộc gọi:
' Module level declaration
Private NameBoxes as TextBox()
Private studentName As String
' Form Load, Form Shown or Sub New:
'
' Using the OP's approach (illegal using OPTION STRICT)
NameBoxes = New TextBox() {Me.Controls("TextBox1"), Me.Controls("TestBox2"), ...)
studentName = TextBox32.Text ' For simple control references
Mã mảng có thể chưa ra khỏi rừng. Bất kỳ điều khiển nào trong điều khiển vùng chứa (như một 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
một lần nữa sẽ được lưu trữ trong các phần tử mảng đó và NRE sẽ dẫn đến kết quả khi bạn cố gắng tham chiếu nó.
Chúng nên dễ dàng tìm thấy ngay bây giờ khi bạn biết những gì bạn đang tìm kiếm:
"Nút2" nằm trên một Panel
Biện pháp khắc phục
Thay vì tham chiếu gián tiếp theo tên bằng cách sử dụng Controls
bộ sưu tập của biểu mẫu , hãy sử dụng tham chiếu điều khiển:
' Declaration
Private NameBoxes As TextBox()
' Initialization - simple and easy to read, hard to botch:
NameBoxes = New TextBox() {TextBox1, TextBox2, ...)
' Initialize a List
NamesList = New List(Of TextBox)({TextBox1, TextBox2, TextBox3...})
' or
NamesList = New List(Of TextBox)
NamesList.AddRange({TextBox1, TextBox2, TextBox3...})
Private bars As New List(Of Bars) ' Declared and created
Public Function BarList() As List(Of Bars)
bars.Clear
If someCondition Then
For n As Integer = 0 to someValue
bars.Add(GetBar(n))
Next n
Else
Exit Function
End If
Return bars
End Function
Đây là trường hợp IDE sẽ cảnh báo bạn rằng ' không phải tất cả các đường dẫn đều trả về giá trị và NullReferenceException
kết quả có thể xảy ra '. Bạn có thể ngăn chặn cảnh báo, bằng cách thay thế Exit 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 nào someCondition = False
sẽ dẫn đến NRE:
bList = myFoo.BarList()
For Each b As Bar in bList ' EXCEPTION
...
Biện pháp khắc phục
Thay thế Exit Function
trong chức năng với Return bList
. Trả lại một sản phẩm nào List
không giống như trả lại Nothing
. Nếu có thể có một đối tượng được trả về Nothing
, hãy kiểm tra trước khi sử dụng nó:
bList = myFoo.BarList()
If bList IsNot Nothing Then...
Thử / Bắt được triển khai kém có thể che giấu vấn đề ở đâu và dẫn đến những vấn đề mới:
Dim dr As SqlDataReader
Try
Dim lnk As LinkButton = TryCast(sender, LinkButton)
Dim gr As GridViewRow = DirectCast(lnk.NamingContainer, GridViewRow)
Dim eid As String = GridView1.DataKeys(gr.RowIndex).Value.ToString()
ViewState("username") = eid
sqlQry = "select FirstName, Surname, DepartmentName, ExtensionName, jobTitle,
Pager, mailaddress, from employees1 where username='" & eid & "'"
If connection.State <> ConnectionState.Open Then
connection.Open()
End If
command = New SqlCommand(sqlQry, connection)
'More code fooing and barring
dr = command.ExecuteReader()
If dr.Read() Then
lblFirstName.Text = Convert.ToString(dr("FirstName"))
...
End If
mpe.Show()
Catch
Finally
command.Dispose()
dr.Close() ' <-- NRE
connection.Close()
End Try
Đây là một trường hợp của một đối tượng không được tạo ra như mong đợi, nhưng cũng cho thấy tính hữu dụng của bộ trống Catch
.
Có một dấu phẩy thừa trong SQL (sau 'mailaddress') dẫn đến ngoại lệ tại .ExecuteReader
. Sau khi 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
là một DataReader
đối tượng null , một NullReferenceException
kết quả hoàn toàn mới .
Một Catch
khối trống là sân chơi của quỷ. OP này đã gặp khó khăn tại sao anh ta nhận được một NRE trong Finally
khối. Trong các tình huống khác, một sản phẩm nào đó Catch
có thể dẫn đến một thứ gì đó khác ở phía dưới sẽ đi sâu hơn và khiến bạn mất thời gian để nhìn vào những thứ sai ở vị trí sai cho vấn đề. ("Ngoại lệ im lặng" được mô tả ở trên cung cấp cùng một giá trị giải trí.)
Biện pháp khắc phục
Không sử dụng các khối Thử / Bắt trống - hãy để mã bị sập để bạn có thể a) xác định nguyên nhân b) xác định vị trí và c) áp dụng một biện pháp khắc phục thích hợp. Các khối Thử / Bắt không nhằm che giấu ngoại lệ khỏi người đủ điều kiện duy nhất để sửa chúng - nhà phát triển.
For Each row As DataGridViewRow In dgvPlanning.Rows
If Not IsDBNull(row.Cells(0).Value) Then
...
Các IsDBNull
chức năng được sử dụng để kiểm tra nếu một giá trị tương đương với System.DBNull
: Từ MSDN:
Giá trị System.DBNull chỉ ra rằng Đối tượng biểu thị dữ liệu bị thiếu hoặc không tồn tại. DBNull không giống như Không có gì, chỉ ra rằng một biến chưa được khởi tạo.
Biện pháp khắc phục
If row.Cells(0) IsNot Nothing Then ...
Như trước đây, bạn có thể kiểm tra Không có gì, sau đó cho một giá trị cụ thể:
If (row.Cells(0) IsNot Nothing) AndAlso (IsDBNull(row.Cells(0).Value) = False) Then
Ví dụ 2
Dim getFoo = (From f In dbContext.FooBars
Where f.something = something
Select f).FirstOrDefault
If Not IsDBNull(getFoo) Then
If IsDBNull(getFoo.user_id) Then
txtFirst.Text = getFoo.first_name
Else
...
FirstOrDefault
trả về mục đầu tiên hoặc giá trị mặc định, Nothing
dành 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 một CheckBox
với chkName
không thể được tìm thấy (hoặc tồn tại trong một GroupBox
), sau đó chk
sẽ có gì và được cố gắng để tham khảo bất kỳ bất động sản sẽ dẫn đến một ngoại lệ.
Biện pháp khắc phục
If (chk IsNot Nothing) AndAlso (chk.Checked) Then ...
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 khắc phục
Đặt tên cho các cột theo cách thủ công hoặc tham chiếu theo chỉ mục:
dgvBooks.Columns(0).Visible = True
xlWorkSheet = xlWorkBook.Sheets("sheet1")
For i = 0 To myDGV.RowCount - 1
For j = 0 To myDGV.ColumnCount - 1
For k As Integer = 1 To myDGV.Columns.Count
xlWorkSheet.Cells(1, k) = myDGV.Columns(k - 1).HeaderText
xlWorkSheet.Cells(i + 2, j + 1) = myDGV(j, i).Value.ToString()
Next
Next
Next
Khi bạn DataGridView
có AllowUserToAddRows
as True
(mặc định), Cells
hàng trống / mới ở phía dưới sẽ chứa tất cả Nothing
. Hầu hết các nỗ lực sử dụng nội dung (ví dụ ToString
:) sẽ dẫn đến NRE.
Biện pháp khắc phục
Sử dụng một For/Each
vòng lặp và kiểm tra thuộc IsNewRow
tính để xác định nếu đó là hàng cuối cùng. Điều này hoạt động cho dù AllowUserToAddRows
là đúng hay không:
For Each r As DataGridViewRow in myDGV.Rows
If r.IsNewRow = False Then
' ok to use this row
Nếu bạn sử dụng một For n
vòng lặp, hãy sửa đổi số hàng hoặc sử dụng Exit For
khi nào IsNewRow
là đúng.
Trong một số trường hợp nhất định, việc cố gắng sử dụng một vật phẩm My.Settings
mà từ đó 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 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ắc phục
Khởi tạo bộ sưu tập cài đặt trong Load
trình xử lý sự kiện của biểu mẫu , nếu / khi cần:
If My.Settings.FooBars Is Nothing Then
My.Settings.FooBars = New System.Collections.Specialized.StringCollection
End If
Thông thường, Settings
bộ sưu tập sẽ chỉ cần được khởi tạo trong lần đầu tiên ứng dụng chạy. Một biện pháp khắc phục thay thế là thêm giá trị ban đầu vào bộ sưu tập của bạn trong Dự án -> Cài đặt | FooBars , lưu dự án, sau đó loại bỏ giá trị giả.
Bạn có thể quên các New
nhà điều hành.
hoặc là
Một cái gì đó bạn giả định sẽ thực hiện hoàn hảo để trả lại một đối tượng được khởi tạo cho mã của bạn, thì không.
Đừng bỏ qua các cảnh báo của trình biên dịch (bao giờ) và sử dụng Option Strict On
(luôn luôn).
Một kịch bản khác là khi bạn chuyển một đối tượng null thành một loại giá trị . Ví dụ: mã dưới đây:
object o = null;
DateTime d = (DateTime)o;
Nó sẽ ném một NullReferenceException
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ã bạn không sở hữu và ví dụ như đúc được tạo bởi một hệ thống tự động.
Một ví dụ về điều này là đoạn liên kết ASP.NET đơn giản này với điều khiển Lịch:
<asp:Calendar runat="server" SelectedDate="<%#Bind("Something")%>" />
SelectedDate
Trên thực tế , đây là một thuộc tính - thuộc DateTime
loại - thuộc Calendar
loại Điều khiển Web và ràng buộc hoàn toàn có thể trả về một cái gì đó không có giá trị. Trình tạo ASP.NET ẩn sẽ tạo ra một đoạn mã tương đương với mã cast ở trên. Và điều này sẽ đưa ra một NullReferenceException
điều khá khó để phát hiện ra, bởi vì nó nằm trong mã được tạo bởi ASP.NET sẽ biên dịch tốt ...
DateTime x = (DateTime) o as DateTime? ?? defaultValue;
Nó có nghĩa là các biến trong câu hỏi được chỉ vào không có gì. Tôi có thể tạo ra điều này như vậy:
SqlConnection connection = null;
connection.Open();
Điều đó sẽ gây ra lỗi vì trong khi tôi đã khai báo biến " connection
", nó không được chỉ ra bất cứ điều gì. Khi tôi cố gắng gọi thành viên " Open
", không có tài liệu tham khảo nào để giải quyết và nó sẽ đưa ra lỗi.
Để tránh lỗi này:
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ữ một tham chiếu đến một thể hiện của bất kỳ đối tượng nào tồn tại trên heap.Bạn không bao giờ được gán một cái gì đó cho biến, không bao giờ tạo một thể hiện của giá trị được gán cho biến hoặc bạn đặt biến bằng
Nothing
/null
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ề ngoại lệ này được đưa ra là: Khi bạn đang cố kiểm tra thứ gì đó, thì đó là null.
Ví dụ:
string testString = null; //Because it doesn't have a value (i.e. it's null; "Length" cannot do what it needs to do)
if (testString.Length == 0) // Throws a nullreferenceexception
{
//Do something
}
Thời gian chạy .NET sẽ đưa ra một NullReferenceException khi bạn cố gắng thực hiện một hành động trên một cái gì đó chưa được khởi tạo tức là mã ở trên.
So với một ArgumentNullException thường được sử dụng như một biện pháp phòng thủ nếu một phương thức mong đợi rằng những gì đang được truyền cho nó không phải là null.
Thông tin thêm có trong C # NullReferenceException và Null Parameter .
Cập nhật C # 8.0, 2019: Các loại tham chiếu Nullable
C # 8.0 giới thiệu các loại tham chiếu nullable và các loại tham chiếu không nullable . Vì vậy, chỉ các loại tham chiếu nullable phải được kiểm tra để tránh NullReferenceException .
Nếu bạn chưa khởi tạo một loại tham chiếu và bạn muốn đặt hoặc đọc một trong các thuộc tính của nó, nó sẽ đưa ra một NullReferenceException .
Thí dụ:
Person p = null;
p.Name = "Harry"; // NullReferenceException occurs here.
Bạn chỉ có thể tránh điều này bằng cách kiểm tra xem biến không phải là null:
Person p = null;
if (p!=null)
{
p.Name = "Harry"; // Not going to run to this point
}
Để hiểu đầy đủ lý do tại sao một NullReferenceException bị ném, điều quan trọng là phải biết sự khác biệt giữa các loại giá trị và [loại tham chiếu] [3].
Vì vậy, nếu bạn đang xử lý các loại giá trị , NullReferenceExceptions không thể xảy ra. Mặc dù bạn cần phải cảnh giác khi làm việc với các loại tham chiếu !
Chỉ các loại tham chiếu, như tên đang gợi ý, có thể giữ các tham chiếu hoặc trỏ theo nghĩa đen vào không có gì (hoặc 'null'). Trong khi các loại giá trị luôn chứa một giá trị.
Các loại tham chiếu (những loại này phải được kiểm tra):
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 NullReferenceExceptions
có thể xảy ra là việc sử dụng (không chính xác) của as
toán tử :
class Book {
public string Name { get; set; }
}
class Car { }
Car mycar = new Car();
Book mybook = mycar as Book; // Incompatible conversion --> mybook = null
Console.WriteLine(mybook.Name); // NullReferenceException
Ở đây, Book
và Car
là các loại không tương thích; a Car
không thể được chuyển đổi / đúc thành a Book
. Khi diễn viên này thất bại, as
trở lại null
. Sử dụng mybook
sau này gây ra a NullReferenceException
.
Nói chung, bạn nên sử dụng một diễn viên hoặc as
, như sau:
Nếu bạn đang mong đợi chuyển đổi loại luôn thành công (nghĩa là bạn biết đối tượng nào sẽ đi trước thời đại), thì bạn nên sử dụng một diễn viên:
ComicBook cb = (ComicBook)specificBook;
Nếu bạn không chắc chắn về loại, nhưng bạn muốn thử sử dụng nó như một loại cụ thể, thì hãy sử dụng as
:
ComicBook cb = specificBook as ComicBook;
if (cb != null) {
// ...
}
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 ()
Trong khi những gì gây ra NullReferenceExceptions và các cách tiếp cận để tránh / sửa một ngoại lệ như vậy đã được giải quyết trong các câu trả lời khác, điều mà nhiều lập trình viên chưa học được là làm thế nào để gỡ lỗi độc lập các ngoại lệ đó trong quá trình phát triển.
Trong Visual Studio, việc này thường dễ dàng nhờ Trình gỡ lỗi Visual Studio .
Trước tiên, hãy đảm bảo rằng lỗi chính xác sẽ bị bắt - xem Làm cách nào để cho phép tôi phá vỡ 'System.NullReferenceException' trong VS2010? Lưu ý 1
Sau đó, Bắt đầu với Gỡ lỗi (F5) hoặc Đính kèm [Trình gỡ lỗi VS] vào Quá trình đang chạy . Đôi khi nó có thể hữu ích để sử dụng Debugger.Break
, nó sẽ nhắc khởi chạy trình gỡ lỗi.
Bây giờ, khi ném NullReferenceException (hoặc chưa xử lý), trình gỡ lỗi sẽ dừng (nhớ quy tắc được đặt ở trên?) Trên dòng xảy ra ngoại lệ. Đôi khi lỗi sẽ dễ dàng nhận ra.
Chẳng hạn, trong dòng sau đây, mã duy nhất có thể gây ra ngoại lệ là nếu myString
ước tính thành null. Điều này có thể được xác minh bằng cách nhìn vào Cửa sổ xem hoặc chạy các biểu thức trong Cửa sổ ngay lập tức .
var x = myString.Trim();
Trong các trường hợp nâng cao hơn, chẳng hạn như sau, bạn sẽ cần sử dụng một trong các kỹ thuật trên (Xem hoặc Windows ngay lập tức) để kiểm tra các biểu thức để xác định xem str1
là null hoặc nếu str2
là null.
var x = str1.Trim() + str2.Trim();
Một khi vị trí ném ngoại lệ được đặt, thường là lý do tầm thường để tìm ra nơi giá trị null được giới thiệu [không chính xác] -
Dành thời gian cần thiết để hiểu nguyên nhân của ngoại lệ. Kiểm tra các biểu thức null. Kiểm tra các biểu thức trước đó có thể dẫn đến các biểu thức null như vậy. Thêm điểm dừng và bước qua chương trình khi thích hợp. Sử dụng trình gỡ lỗi.
1 Nếu Break on Ném quá hung hăng và trình gỡ lỗi dừng trên NPE trong thư viện .NET hoặc bên thứ 3, Break on User-Unhandled có thể được sử dụng để hạn chế các ngoại lệ bị bắt. Ngoài ra, VS2012 cũng giới thiệu Just My Code mà tôi khuyên bạn cũng nên bật.
Nếu bạn đang gỡ lỗi khi kích hoạt Just My Code, hành vi sẽ hơi khác. Khi Just My Code được bật, trình gỡ lỗi bỏ qua các ngoại lệ thời gian chạy ngôn ngữ chung (CLR) cơ hội đầu tiên được đưa ra ngoài Mã của tôi và không chuyển qua Mã của tôi
Simon Mourier đã đưa ra ví dụ này :
object o = null;
DateTime d = (DateTime)o; // NullReferenceException
trong đó một chuyển đổi unboxing (truyền) từ object
(hoặc từ một trong các lớp System.ValueType
hoặc System.Enum
, hoặc từ một loại giao diện) sang một loại giá trị (khác Nullable<>
) tự nó cung cấp cho NullReferenceException
.
Theo hướng khác, một chuyển đổi quyền anh từ một Nullable<>
loại HasValue
tương đương false
với một loại tham chiếu, có thể đưa ra một 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 Nullable<>
trường hợp quyền anh .
Thêm trường hợp khi tên lớp cho thực thể được sử dụng trong khung thực thể giống như tên lớp cho tệp mã phía sau của biểu mẫu web.
Giả sử bạn có một biểu mẫu web Contact.aspx có lớp codebehind là Liên hệ và bạn có một tên thực thể Liên hệ.
Sau đó, đoạn mã sau sẽ đưa ra một NullReferenceException khi bạn gọi bối cảnh.SaveChanges ()
Contact contact = new Contact { Name = "Abhinav"};
var context = new DataContext();
context.Contacts.Add(contact);
context.SaveChanges(); // NullReferenceException at this line
Để hoàn thiện lớp DataContext
public class DataContext : DbContext
{
public DbSet<Contact> Contacts {get; set;}
}
và Liên hệ lớp thực thể. Đôi khi các lớp thực thể là các lớp một phần để bạn cũng có thể mở rộng chúng trong các tệp khác.
public partial class Contact
{
public string Name {get; set;}
}
Lỗi xảy ra khi cả lớp thực thể và lớp cơ sở trong cùng một không gian tên. Để sửa lỗi này, đổi tên lớp thực thể hoặc lớp codebehind cho Contact.aspx.
Lý do tôi vẫn không chắc chắn về lý do. Nhưng bất cứ khi nào bất kỳ lớp thực thể nào sẽ mở rộng System.Web.UI.Page thì lỗi này xảy ra.
Để thảo luận, hãy xem NullReferenceException trong DbContext.saveChanges ()
Một trường hợp chung khác mà người ta có thể nhận được ngoại lệ này liên quan đến các lớp chế nhạo trong quá trình kiểm tra đơn vị. Bất kể khung mô phỏng đang được sử dụng, bạn phải đảm bảo rằng tất cả các mức phù hợp của hệ thống phân cấp lớp được chế giễu đúng. Cụ thể, tất cả các thuộc tính HttpContext
được tham chiếu bởi mã được kiểm tra phải được chế giễu.
Xem " NullReferenceException được ném khi kiểm tra AuthorizationAttribution tùy chỉnh " để biết ví dụ hơi dài dòng.
Tôi có một quan điểm khác nhau để trả lời điều này. Loại câu trả lời này "tôi có thể làm gì khác để tránh nó? "
Khi làm việc trên các lớp khác nhau , ví dụ như trong ứng dụng MVC, bộ điều khiển cần các dịch vụ để gọi các hoạt động kinh doanh. Trong các trường hợp như vậy, Dependency Injection Container có thể được sử dụng để khởi tạo các dịch vụ nhằm tránh NullReferenceException . Vì vậy, điều đó có nghĩa là bạn không cần phải lo lắng về việc kiểm tra null và chỉ cần gọi các dịch vụ từ bộ điều khiển như thể chúng sẽ luôn có sẵn (và được khởi tạo) dưới dạng đơn lẻ hoặc nguyên mẫu.
public class MyController
{
private ServiceA serviceA;
private ServiceB serviceB;
public MyController(ServiceA serviceA, ServiceB serviceB)
{
this.serviceA = serviceA;
this.serviceB = serviceB;
}
public void MyMethod()
{
// We don't need to check null because the dependency injection container
// injects it, provided you took care of bootstrapping it.
var someObject = serviceA.DoThis();
}
}
Về vấn đề "tôi nên làm gì với nó" , có thể có nhiều câu trả lời.
Một cách "chính thức" hơn để ngăn chặn các điều kiện lỗi như vậy trong khi phát triển là áp dụng thiết kế theo hợp đồng trong mã của bạn. Điều này có nghĩa là bạn cần thiết lập các bất biến lớp và / hoặc thậm chí các điều kiện tiên quyết về chức năng / phương thức và hậu điều kiện trên hệ thống của bạn, trong khi phát triển.
Nói tóm lại, các bất biến lớp đảm bảo rằng sẽ có một số ràng buộc trong lớp của bạn sẽ không bị vi phạm trong sử dụng bình thường (và do đó, lớp sẽ không ở trạng thái không nhất quán). Điều kiện tiên quyết có nghĩa là dữ liệu được cung cấp làm đầu vào cho một hàm / phương thức phải tuân theo một số ràng buộc được đặt và không bao giờ vi phạm chúng, và hậu điều kiện có nghĩa là đầu ra của hàm / phương thức phải tuân theo các ràng buộc đã đặt lại mà không bao giờ vi phạm chúng. Các điều kiện hợp đồng không bao giờ bị vi phạm trong khi thực hiện chương trình không có lỗi, do đó, thiết kế theo hợp đồng được kiểm tra trong thực tế ở chế độ gỡ lỗi, trong khi bị vô hiệu hóa trong các bản phát hành , để tối đa hóa hiệu suất hệ thống được phát triển.
Bằng cách này, bạn có thể tránh NullReferenceException
các trường hợp là kết quả vi phạm các ràng buộc được đặt. Ví dụ: nếu bạn sử dụng một X
thuộc tính đối tượng trong một lớp và sau đó cố gắng gọi một trong các phương thức của nó và 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 Hợp đồng mã tồn tại cho các ứng dụng .NET.
Ngoài ra, thiết kế theo hợp đồng có thể được áp dụng bằng cách sử dụng các xác nhận .
CẬP NHẬT: Điều đáng nói là thuật ngữ này được đặt ra bởi Bertrand Meyer liên quan đến thiết kế ngôn ngữ lập trình Eiffel của ông .
A NullReferenceException
bị ném khi chúng ta đang cố truy cập vào Thuộc tính của đối tượng null hoặc khi giá trị chuỗi trở nên trống rỗng và chúng ta đang cố gắng truy cập các phương thức chuỗi.
Ví dụ:
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
String.Empty.ToLower()
sẽ không ném ngoại lệ tham chiếu null. Nó đại diện cho một chuỗi thực tế, mặc dù một chuỗi trống (nghĩa là ""
). Vì điều này có một đối tượng để gọi ToLower()
, nên sẽ không có ý nghĩa gì khi ném ngoại lệ tham chiếu null vào đó.
TL; DR: Hãy 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ư thế này:
@{
MyEntity M = new MyEntity();
}
@RenderPage("_MyOtherView.cshtml", M); // error in _MyOtherView, the Model was Null
Gỡ lỗi cho thấy mô hình là Null bên trong MyOtherView. Cho đến khi tôi đổi nó thành:
@{
MyEntity M = new MyEntity();
}
@Html.Partial("_MyOtherView.cshtml", M);
Va no đa hoạt động.
Hơn nữa, lý do tôi không phải Html.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 Html.Partial
nếu nó nằm trong một foreach
vòng lặp được xây dựng khác , mặc dù nó không thực sự là một lỗi:
@inherits System.Web.Mvc.WebViewPage
@{
ViewBag.Title = "Entity Index";
List<MyEntity> MyEntities = new List<MyEntity>();
MyEntities.Add(new MyEntity());
MyEntities.Add(new MyEntity());
MyEntities.Add(new MyEntity());
}
<div>
@{
foreach(var M in MyEntities)
{
// Squiggly lines below. Hovering says: cannot convert method group 'partial' to non-delegate type Object, did you intend to envoke the Method?
@Html.Partial("MyOtherView.cshtml");
}
}
</div>
Nhưng tôi đã có thể chạy ứng dụng mà không gặp vấn đề gì với "lỗi" này. Tôi đã có thể thoát khỏi lỗi bằng cách thay đổi cấu trúc của foreach
vòng lặp để trông như thế này:
@foreach(var M in MyEntities){
...
}
Mặc dù tôi có cảm giác đó là vì Visual Studio đã đọc sai các ký hiệu và dấu ngoặc.
Html.Partial
, không phải@Html.Partial
Null
vì vậy), vì vậy tôi biết lỗi là do tôi đã gửi Mô hình vào.
Bạn có thể làm gì về nó?
Có rất nhiều câu trả lời hay ở đây giải thích thế nào là một tài liệu tham khảo null và cách gỡ lỗi nó. Nhưng có rất ít về cách ngăn chặn vấn đề hoặc ít nhất là làm cho nó dễ nắm bắt hơn.
Kiểm tra đối số
Ví dụ, các phương thức có thể kiểm tra các đối số khác nhau để xem liệu chúng có rỗng không và ném ra ArgumentNullException
, một ngoại lệ rõ ràng được tạo ra cho mục đích chính xác này.
Hàm tạo cho ArgumentNullException
chẵn lấy tên của tham số và thông báo làm đối số để bạn có thể cho nhà phát triển biết chính xác vấn đề là gì.
public void DoSomething(MyObject obj) {
if(obj == null)
{
throw new ArgumentNullException("obj", "Need a reference to obj.");
}
}
Sử dụng công cụ
Ngoài ra còn có một số thư viện có thể giúp đỡ. Ví dụ: "Chia sẻ lại" có thể cung cấp cho bạn các cảnh báo trong khi bạn đang viết mã, đặc biệt nếu bạn sử dụng thuộc tính của chúng: NotNullAttribution
Có "Hợp đồng mã Microsoft" nơi bạn sử dụng cú pháp như Contract.Requires(obj != null)
cung cấp cho bạn thời gian chạy và kiểm tra biên dịch: Giới thiệu Hợp đồng mã .
Ngoài ra còn có "PostSharp" cho phép bạn chỉ sử dụng các thuộc tính như thế này:
public void DoSometing([NotNull] obj)
Bằng cách thực hiện điều đó và làm cho PostSharp trở thành một phần của quá trình xây dựng của bạn obj
sẽ được kiểm tra null khi chạy. Xem: Kiểm tra null PostSharp
Giải pháp mã đồng bằng
Hoặc bạn luôn có thể viết mã phương pháp của riêng bạn bằng cách sử dụng mã cũ đơn giản. Ví dụ ở đây là một cấu trúc mà bạn có thể sử dụng để bắt các tham chiếu null. Nó được mô phỏng theo khái niệm tương tự như Nullable<T>
:
[System.Diagnostics.DebuggerNonUserCode]
public struct NotNull<T> where T: class
{
private T _value;
public T Value
{
get
{
if (_value == null)
{
throw new Exception("null value not allowed");
}
return _value;
}
set
{
if (value == null)
{
throw new Exception("null value not allowed.");
}
_value = value;
}
}
public static implicit operator T(NotNull<T> notNullValue)
{
return notNullValue.Value;
}
public static implicit operator NotNull<T>(T value)
{
return new NotNull<T> { Value = value };
}
}
Bạn sẽ sử dụng rất giống với cách bạn sẽ sử dụng Nullable<T>
, ngoại trừ với mục tiêu hoàn thành chính xác điều ngược lại - không cho phép null
. Dưới đây là một số ví dụ:
NotNull<Person> person = null; // throws exception
NotNull<Person> person = new Person(); // OK
NotNull<Person> person = GetPerson(); // throws exception if GetPerson() returns null
NotNull<T>
được mặc nhiên chuyển đến và từ T
đó bạn có thể sử dụng nó ở bất cứ đâu bạn cần. Ví dụ: bạn có thể truyền một Person
đối tượng cho một phương thức có một NotNull<Person>
:
Person person = new Person { Name = "John" };
WriteName(person);
public static void WriteName(NotNull<Person> person)
{
Console.WriteLine(person.Value.Name);
}
Như bạn có thể thấy ở trên như với nullable, bạn sẽ truy cập giá trị cơ bản thông qua thuộc Value
tính. Ngoài ra, bạn có thể sử dụng một diễn viên rõ ràng hoặc ẩn, bạn có thể xem một ví dụ với giá trị trả về bên dưới:
Person person = GetPerson();
public static NotNull<Person> GetPerson()
{
return new Person { Name = "John" };
}
Hoặc thậm chí bạn có thể sử dụng nó khi phương thức chỉ trả về T
(trong trường hợp này Person
) bằng cách thực hiện cast. Ví dụ: đoạn mã sau sẽ giống như đoạn mã trên:
Person person = (NotNull<Person>)GetPerson();
public static Person GetPerson()
{
return new Person { Name = "John" };
}
Kết hợp với phần mở rộng
Kết hợp NotNull<T>
với một phương pháp mở rộng và bạn có thể bao quát nhiều tình huống hơn nữa. Dưới đây là một ví dụ về phương thức mở rộng có thể trông như thế nào:
[System.Diagnostics.DebuggerNonUserCode]
public static class NotNullExtension
{
public static T NotNull<T>(this T @this) where T: class
{
if (@this == null)
{
throw new Exception("null value not allowed");
}
return @this;
}
}
Và đây là một ví dụ về cách nó có thể được sử dụng:
var person = GetPerson().NotNull();
GitHub
Để bạn tham khảo, tôi đã cung cấp mã ở trên trên GitHub, bạn có thể tìm thấy nó tại:
https://github.com/luisperezphd/NotNull
Tính năng ngôn ngữ liên quan
C # 6.0 đã giới thiệu "toán tử có điều kiện null" giúp điều này một chút. Với tính năng này, bạn có thể tham chiếu các đối tượng lồng nhau và nếu bất kỳ một trong số chúng là null
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 của loại Country
có một thuộc tính được gọi State
và như vậy. Nếu country
, State
, County
, hoặc City
là null
sau đó address will be
vô . Therefore you only have to check whether
địa chỉ is
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 xây dựng như Nullable?
C # có một tốc ký tuyệt vời Nullable<T>
, bạn có thể làm cho một cái gì đó không thể bằng cách đặt một dấu hỏi sau kiểu như vậy int?
.
Sẽ thật tuyệt nếu C # có một cái gì đó giống như NotNull<T>
cấu trúc ở trên và có một tốc ký tương tự, có thể là dấu chấm than (!) Để bạn có thể viết một cái gì đó như : public void WriteName(Person! person)
.
Thật thú vị, không có câu trả lời nào trên trang này đề cập đến hai trường hợp cạnh, hy vọng không ai bận tâm nếu tôi thêm chúng:
Từ điển chung trong .NET không an toàn cho luồng và đôi khi chúng có thể ném một NullReference
hoặc thậm chí (thường xuyên hơn) 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 a NullReferenceException
bị ném bởi unsafe
mã, bạn có thể nhìn vào các biến con trỏ của mình và kiểm tra xem chúng có IntPtr.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 các loại giá trị / mảng, v.v., và bạn đập đầu vào tường, tự hỏi làm thế nào một loại giá trị có thể ném cái này ngoại lệ.
(Một lý do khác để không sử dụng mã không an toàn trừ khi bạn cần nó, nhân tiện)
null
cách nào?
Bạn có thể sửa lỗi NullReferenceException một cách rõ ràng bằng cách sử dụng các toán tử điều kiện Null trong c # 6 và viết ít mã hơn để xử lý kiểm tra null.
Nó được sử dụng để kiểm tra null trước khi thực hiện thao tác truy cập thành viên (?.) Hoặc chỉ mục (? [).
Thí dụ
var name = p?.Spouse?.FirstName;
tương đương với:
if (p != null)
{
if (p.Spouse != null)
{
name = p.Spouse.FirstName;
}
}
Kết quả là tên sẽ là null khi p là null hoặc khi p.Spouse là null.
Nếu không, tên biến sẽ được gán giá trị của p.Spouse.FirstName.
Để biết thêm chi tiết: Toán tử không có điều kiện
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;
}
}
Tham chiếu NullReferenceException hoặc Object không được đặt thành phiên bản của đối tượng xảy ra khi một đối tượng của lớp bạn đang cố sử dụng không được khởi tạo. Ví dụ:
Giả sử rằng bạn có một lớp có tên là Sinh viên.
public class Student
{
private string FirstName;
private string LastName;
public string GetFullName()
{
return FirstName + LastName;
}
}
Bây giờ, hãy xem xét một lớp học khác mà bạn đang cố lấy lại tên đầy đủ của học sinh.
public class StudentInfo
{
public string GetStudentName()
{
Student s;
string fullname = s.GetFullName();
return fullname;
}
}
Như đã thấy trong đoạn mã trên, câu lệnh Student s - chỉ khai báo biến loại Sinh viên, lưu ý rằng lớp Sinh viên không được khởi tạo tại thời điểm này. Do đó, khi câu lệnh s.GetFullName () được thực thi, nó sẽ ném NullReferenceException.
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 mới ở 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ếu GC đã thu thập đối tượng rồi ... Điều này thường xảy ra nếu bạn đang cố gắng tìm một đối tượng bằng chuỗi ... Đó là, tìm nó theo tên của đối tượng thì có thể xảy ra rằng GC có thể đã xảy ra làm sạch nó ... Điều này rất khó tìm và sẽ trở thành một vấn đề ... Cách tốt hơn để giải quyết vấn đề này là kiểm tra null bất cứ khi nào cần thiết trong quá trình phát triển. Điều này sẽ giúp bạn tiết kiệm rất nhiều thời gian.
Bằng cách tìm theo tên tôi có nghĩa là một số khung cho phép bạn FIndObjects sử dụng chuỗi và mã có thể trông như thế này: FindObject ("ObjectName");
Theo nghĩa đen, cách dễ nhất để sửa lỗi NullReferenceExellect có hai cách. Nếu bạn có GameObject chẳng hạn với tập lệnh được đính kèm và một biến có tên rb (Rigidbody) thì biến này sẽ bắt đầu null khi bạn bắt đầu trò chơi của mình.
Đây là lý do tại sao bạn nhận được NullReferenceExellect vì máy tính không có dữ liệu được lưu trữ trong biến đó.
Tôi sẽ sử dụng biến RigidBody làm ví dụ.
Chúng tôi có thể thêm dữ liệu thực sự dễ dàng thực sự theo một số cách:
rb = GetComponent<Rigidbody>();
Start()
hoặc Awake()
chức năng 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).
Thưởng thức và vui chơi làm trò chơi!
Nếu chúng ta xem xét các tình huống phổ biến trong đó ngoại lệ này có thể được ném, truy cập các thuộc tính héo đối tượng ở trên cùng.
Ví dụ:
string postalcode=Customer.Address.PostalCode;
//if customer or address is null , this will through exeption
ở đây, nếu địa chỉ là null, thì bạn sẽ nhận được NullReferenceException.
Vì vậy, như một thông lệ, chúng ta nên luôn luôn sử dụng kiểm tra null, trước khi truy cập các thuộc tính trong các đối tượng đó (đặc biệt chung chung)
string postalcode=Customer?.Address?.PostalCode;
//if customer or address is null , this will return null, without through a exception
Đây là một ngoại lệ tham chiếu Null . Như Microsoft tuyên bố-
Một ngoại lệ NullReferenceException được đưa ra khi bạn cố gắng truy cập một thành viên của loại có giá trị là null.
Điều đó có nghĩa là nếu bất kỳ thành viên nào không giữ bất kỳ giá trị nào và chúng tôi đang khiến thành viên đó thực hiện một số nhiệm vụ nhất định thì hệ thống chắc chắn sẽ ném một tin nhắn và nói-
Hãy chờ đợi, thành viên đó không có giá trị nên không thể thực hiện nhiệm vụ mà bạn đang bàn giao.
Chính ngoại lệ nói rằng một cái gì đó đang được đề cập nhưng giá trị của nó không được đặt. Vì vậy, điều này biểu thị rằng nó chỉ xảy ra trong khi sử dụng các loại tham chiếu làm Loại giá trị là không thể rỗng.
NullReferenceException sẽ không xảy ra nếu chúng ta đang sử dụng các thành viên loại Giá trị.
class Program
{
static void Main(string[] args)
{
string str = null;
Console.WriteLine(str.Length);
Console.ReadLine();
}
}
Đoạn mã trên hiển thị chuỗi đơn giản được gán với giá trị null .
Bây giờ, khi tôi cố gắng in độ dài của chuỗi str , tôi nhận được một ngoại lệ chưa được xử lý của loại thông báo 'System.NullReferenceException' vì str thành viên đang trỏ đến null và không thể có độ dài null.
' NullReferenceException ' cũng xảy ra khi chúng ta quên khởi tạo một kiểu tham chiếu.
Giả sử tôi có một phương thức lớp và thành viên trong đó. Tôi đã không ngay lập tức lớp học của tôi mà chỉ đặt tên lớp học của tôi. Bây giờ nếu tôi cố gắng sử dụng phương thức, trình biên dịch sẽ đưa ra lỗi hoặc đưa ra cảnh báo (tùy thuộc vào trình biên dịch).
class Program
{
static void Main(string[] args)
{
MyClass1 obj;
obj.foo(); //Use of unassigned local variable 'obj'
}
}
public class MyClass1
{
internal void foo()
{
Console.WriteLine("hello from foo");
}
}
Trình biên dịch cho mã ở trên làm phát sinh lỗi rằng biến obj không được gán mà biểu thị rằng biến của chúng ta có giá trị null hoặc không có gì. Trình biên dịch cho mã ở trên làm phát sinh lỗi rằng biến obj không được gán mà biểu thị rằng biến của chúng ta có giá trị null hoặc không có gì.
NullReferenceException phát sinh do lỗi của chúng tôi vì không kiểm tra giá trị của đối tượng. Chúng ta thường bỏ chọn các giá trị đối tượng trong quá trình phát triển mã.
Nó cũng phát sinh khi chúng ta quên khởi tạo các đối tượng của chúng ta. Sử dụng các phương thức, thuộc tính, bộ sưu tập, vv có thể trả về hoặc đặt giá trị null cũng có thể là nguyên nhân của ngoại lệ này.
Có nhiều cách và phương pháp khác nhau để tránh ngoại lệ nổi tiếng này:
Kiểm tra rõ ràng: Chúng ta nên tuân thủ truyền thống kiểm tra các đối tượng, thuộc tính, phương thức, mảng và bộ sưu tập cho dù chúng là null. Điều này có thể được thực hiện đơn giản bằng cách sử dụng các câu lệnh có điều kiện như if-other if-other, v.v.
Xử lý ngoại lệ: Một trong những cách quan trọng để quản lý ngoại lệ này. Sử dụng các khối thử-bắt-cuối đơn giản, chúng ta có thể kiểm soát ngoại lệ này và cũng duy trì nhật ký của nó. Điều này có thể rất hữu ích khi ứng dụng của bạn đang ở giai đoạn sản xuất.
Toán tử Null: Toán tử Null Coalescing và toán tử điều kiện null cũng có thể được sử dụng một cách hữu ích trong khi đặt giá trị cho các đối tượng, biến, thuộc tính và trường.
Trình gỡ lỗi: Đối với các nhà phát triển, chúng tôi có vũ khí lớn để gỡ lỗi với chúng tôi. Nếu chúng ta phải đối mặt với NullReferenceException trong quá trình phát triển, chúng ta có thể sử dụng trình gỡ lỗi để lấy nguồn của ngoại lệ.
Phương thức dựng sẵn: Các phương thức hệ thống như GetValueOrDefault (), IsNullOrWhiteSpace () và IsNullorEmpty () kiểm tra null và gán giá trị mặc định nếu có giá trị null.
Có rất nhiều câu trả lời tốt ở đây. Bạn cũng có thể kiểm tra mô tả chi tiết hơn với các ví dụ trên blog của tôi .
Hy vọng điều này sẽ giúp quá!
Nếu ai đó nhận được thông báo này trong khi lưu hoặc biên dịch bản dựng, chỉ cần đóng tất cả các tệp và sau đó mở bất kỳ tệp nào để biên dịch và lưu.
Đối với tôi lý do là tôi đã đổi tên tập tin và tập tin cũ vẫn mở.