ฉันมีโค้ดบางส่วนและเมื่อมันประมวลผลมันจะพ่น a NullReferenceException
โดยพูดว่า:
การอ้างอิงวัตถุไม่ได้ถูกตั้งค่าเป็นอินสแตนซ์ของวัตถุ
สิ่งนี้หมายความว่าอะไรและฉันจะทำอย่างไรเพื่อแก้ไขข้อผิดพลาดนี้
ฉันมีโค้ดบางส่วนและเมื่อมันประมวลผลมันจะพ่น a NullReferenceException
โดยพูดว่า:
การอ้างอิงวัตถุไม่ได้ถูกตั้งค่าเป็นอินสแตนซ์ของวัตถุ
สิ่งนี้หมายความว่าอะไรและฉันจะทำอย่างไรเพื่อแก้ไขข้อผิดพลาดนี้
คำตอบ:
คุณกำลังพยายามใช้สิ่งที่null
(หรือNothing
ใน VB.NET) ซึ่งหมายความว่าคุณสามารถตั้งค่าเป็นnull
หรือคุณไม่เคยกำหนดเป็นอะไรเลย
เหมือนอะไรก็ได้null
ผ่านไป หากอยู่null
ในวิธี "A" อาจเป็นได้ว่าวิธี "B" ส่งผ่านnull
ไปยังวิธี "A"
null
สามารถมีความหมายต่างกัน:
NullReferenceException
ในกรณีนี้ถ้าคุณเข้าถึงคุณสมบัติหรือวิธีการของวัตถุดังกล่าวจะทำให้เกิดการnull
ความตั้งใจในการระบุว่าไม่มีคุณค่าที่มีความหมาย โปรดทราบว่า C # มีแนวคิดของประเภทข้อมูลที่เป็นโมฆะสำหรับตัวแปร (เช่นตารางฐานข้อมูลสามารถมีเขตข้อมูลที่เป็นโมฆะได้) - คุณสามารถกำหนดให้null
กับพวกเขาเพื่อระบุว่าไม่มีค่าที่เก็บอยู่ในนั้นตัวอย่างเช่นint? a = null;
เครื่องหมายคำถามระบุว่า a
ตัวแปร คุณสามารถตรวจสอบว่าทั้งที่มีหรือif (a.HasValue) {...}
if (a==null) {...}
ตัวแปร Nullable เช่นa
ตัวอย่างนี้จะช่วยให้การเข้าถึงค่าผ่านทางอย่างชัดเจนหรือเพียงแค่เป็นปกติผ่านทางa.Value
โปรดทราบว่าการเข้าถึงผ่านทางพ่นแทนถ้าเป็นa
a.Value
InvalidOperationException
NullReferenceException
a
null
- คุณควรจะตรวจสอบก่อนเช่นถ้าคุณมีอีกตัวแปรใน nullable int b;
แล้วคุณควรจะทำอย่างไรที่ได้รับมอบหมายเช่นหรือสั้นif (a.HasValue) { b = a.Value; }
if (a != null) { b = a; }
ส่วนที่เหลือของบทความนี้ไปในรายละเอียดและการแสดงอื่น ๆ NullReferenceException
อีกมากมายข้อผิดพลาดที่โปรแกรมเมอร์จำนวนมากมักจะทำซึ่งสามารถนำไปสู่การ
การruntime
ขว้าง a หมายถึงสิ่งเดียวกันNullReferenceException
เสมอ : คุณกำลังพยายามใช้การอ้างอิงและการอ้างอิงนั้นไม่เริ่มต้น (หรือเริ่มต้นครั้งเดียวแต่ไม่ได้เริ่มต้นอีกต่อไป )
ซึ่งหมายความว่าการอ้างอิงคือnull
และคุณไม่สามารถเข้าถึงสมาชิก (เช่นวิธีการ) ผ่านการnull
อ้างอิง กรณีที่ง่ายที่สุด:
string foo = null;
foo.ToUpper();
นี้จะโยนNullReferenceException
ในบรรทัดที่สองเพราะคุณไม่สามารถเรียกวิธีการเช่นToUpper()
ในชี้อ้างอิงถึงstring
null
คุณจะหาที่มาของ a ได้NullReferenceException
อย่างไร? นอกเหนือจากการดูข้อยกเว้นซึ่งจะถูกโยนตรงตำแหน่งที่เกิดขึ้นกฎทั่วไปของการดีบักใน Visual Studio ใช้: วางจุดพักเชิงกลยุทธ์และตรวจสอบตัวแปรของคุณไม่ว่าจะด้วยการวางเมาส์เหนือชื่อของพวกเขาเปิดตัว ( ด่วน) ดูหน้าต่างหรือใช้แผงแก้ไขจุดบกพร่องต่าง ๆ เช่น Locals และ Autos
หากคุณต้องการทราบว่าการตั้งค่าการอ้างอิงอยู่หรือไม่ให้คลิกขวาที่ชื่อและเลือก "ค้นหาการอ้างอิงทั้งหมด" จากนั้นคุณสามารถวางเบรกพอยต์ในทุกตำแหน่งที่พบและเรียกใช้โปรแกรมของคุณโดยแนบดีบักเกอร์ ทุกครั้งที่ดีบักเกอร์หยุดพักบนเบรกพอยต์คุณต้องพิจารณาว่าคุณคาดหวังว่าการอ้างอิงนั้นไม่เป็นโมฆะตรวจสอบตัวแปรและตรวจสอบว่ามีการชี้ไปที่อินสแตนซ์เมื่อคุณต้องการหรือไม่
ด้วยการติดตามโฟลว์ของโปรแกรมด้วยวิธีนี้คุณสามารถค้นหาตำแหน่งที่อินสแตนซ์ไม่ควรเป็นโมฆะและสาเหตุที่ตั้งค่าไม่ถูกต้อง
สถานการณ์ทั่วไปบางอย่างที่สามารถโยนข้อยกเว้นได้:
ref1.ref2.ref3.member
หาก ref1 หรือ REF2 หรือ ref3 NullReferenceException
เป็นโมฆะแล้วคุณจะได้รับ หากคุณต้องการแก้ปัญหาให้ค้นหาว่าอันใดที่เป็นโมฆะโดยเขียนนิพจน์ใหม่ให้เทียบเท่าง่ายกว่า:
var r1 = ref1;
var r2 = r1.ref2;
var r3 = r2.ref3;
r3.member
โดยเฉพาะในการHttpContext.Current.User.Identity.Name
ที่HttpContext.Current
จะเป็นโมฆะหรือUser
สถานที่ให้บริการจะเป็นโมฆะหรือIdentity
สถานที่ให้บริการจะเป็นโมฆะ
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.
}
}
ถ้าคุณต้องการหลีกเลี่ยงการอ้างอิง null (บุคคล) เด็กคุณสามารถเริ่มต้นได้ในตัวสร้างวัตถุหลัก (หนังสือ)
เช่นเดียวกับวัตถุเริ่มต้นที่ซ้อนกัน:
Book b1 = new Book
{
Author = { Age = 45 }
};
สิ่งนี้แปลว่า
Book b1 = new Book();
b1.Author.Age = 45;
ในขณะที่ใช้new
คำหลักมันจะสร้างเพียงอินสแตนซ์ใหม่Book
แต่ไม่ใช่อินสแตนซ์ใหม่Person
ดังนั้นAuthor
คุณสมบัติยังคงnull
อยู่
public class Person
{
public ICollection<Book> Books { get; set; }
}
public class Book
{
public string Title { get; set; }
}
คอลเล็กชันที่ซ้อนกันInitializers
ทำหน้าที่เหมือนกัน:
Person p1 = new Person
{
Books = {
new Book { Title = "Title1" },
new Book { Title = "Title2" },
}
};
สิ่งนี้แปลว่า
Person p1 = new Person();
p1.Books.Add(new Book { Title = "Title1" });
p1.Books.Add(new Book { Title = "Title2" });
new Person
สร้างเพียงตัวอย่างของPerson
แต่คอลเลกชันยังคงเป็นBooks
ไวยากรณ์null
คอลเลกชันInitializer
ไม่ได้สร้างคอลเลกชันสำหรับp1.Books
มันจะแปลเป็นp1.Books.Add(...)
คำสั่งเท่านั้น
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.
Form1 ชั้นสาธารณะ {ลูกค้าลูกค้าส่วนตัว;
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);
}
}
สิ่งนี้สามารถแก้ไขได้โดยทำตามการประชุมเพื่อนำหน้าฟิลด์ด้วยเครื่องหมายขีดล่าง:
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();
หากข้อยกเว้นเกิดขึ้นเมื่ออ้างอิงคุณสมบัติของ@Model
ในASP.NET MVC View
คุณต้องเข้าใจว่าการModel
ตั้งค่าในวิธีการกระทำของคุณเมื่อคุณreturn
ดู เมื่อคุณส่งคืนโมเดลว่างเปล่า (หรือคุณสมบัติโมเดล) จากคอนโทรลเลอร์ของคุณข้อยกเว้นจะเกิดขึ้นเมื่อมุมมองเข้าถึงมัน:
// 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
ตัวควบคุมจะถูกสร้างขึ้นในระหว่างการเรียกไปInitializeComponent
ตามลำดับที่ปรากฏในแผนภูมิที่มองเห็น A NullReferenceException
จะถูกยกขึ้นในกรณีของการควบคุมที่สร้างขึ้นก่อนหน้านี้พร้อมกับตัวจัดการเหตุการณ์ ฯลฯInitializeComponent
ซึ่งจะทำงานระหว่างการอ้างอิงการควบคุมที่สร้างขึ้นล่าช้า
ตัวอย่างเช่น :
<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>
ที่นี่ถูกสร้างขึ้นก่อนcomboBox1
label1
หากcomboBox1_SelectionChanged
ความพยายามในการอ้างอิง `label1 มันจะยังไม่ถูกสร้างขึ้น
private void comboBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
label1.Content = comboBox1.SelectedIndex.ToString(); // NullReference here!!
}
การเปลี่ยนลำดับของการประกาศในXAML
(เช่นรายการlabel1
ก่อนหน้าการcomboBox1
เพิกเฉยปัญหาปรัชญาการออกแบบอย่างน้อยก็จะแก้ไขที่NullReferenceException
นี่
as
var myThing = someObject as Thing;
นี่ไม่ใช่การโยนInvalidCastException
แต่ส่งคืนnull
เมื่อการร่ายล้มเหลว (และเมื่อsomeObject
ตัวเองเป็นโมฆะ) ดังนั้นจงระวังให้ดี
FirstOrDefault()
และSingleOrDefault()
รุ่นธรรมดาFirst()
และSingle()
โยนข้อยกเว้นเมื่อไม่มีอะไร เวอร์ชัน "OrDefault" ส่งคืนค่า null ในกรณีนั้น ดังนั้นจงระวังให้ดี
foreach
โยนเมื่อคุณพยายามที่จะย้ำคอลเลกชันโมฆะ มักเกิดจากnull
ผลลัพธ์ที่ไม่คาดคิดจากวิธีการที่ส่งคืนคอลเลกชัน
List<int> list = null;
foreach(var v in list) { } // exception
ตัวอย่างที่สมจริงยิ่งขึ้น - เลือกโหนดจากเอกสาร XML จะโยนหากไม่พบโหนด แต่การดีบั๊กเริ่มต้นแสดงให้เห็นว่าคุณสมบัติทั้งหมดนั้นถูกต้อง:
foreach (var node in myData.MyXml.DocumentNode.SelectNodes("//Data"))
null
และละเว้นค่า Null อย่างชัดเจนหากคุณคาดว่าบางครั้งการอ้างอิงจะเป็นโมฆะคุณสามารถตรวจสอบได้ว่าเป็นข้อมูลnull
ก่อนเข้าถึงสมาชิกอินสแตนซ์:
void PrintName(Person p)
{
if (p != null)
{
Console.WriteLine(p.Name);
}
}
null
และระบุค่าเริ่มต้นเมธอดที่คุณคาดว่าจะส่งคืนอินสแตนซ์สามารถส่งคืนได้null
ตัวอย่างเช่นเมื่อไม่พบวัตถุที่ต้องการ คุณสามารถเลือกที่จะคืนค่าเริ่มต้นเมื่อเป็นกรณีนี้:
string GetCategory(Book b)
{
if (b == null)
return "Unknown";
return b.Category;
}
null
จากการเรียกใช้เมธอดและโยนข้อยกเว้นที่กำหนดเองนอกจากนี้คุณยังสามารถโยนข้อยกเว้นที่กำหนดเองเพื่อจับในรหัสการโทรเท่านั้น:
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
หากค่าไม่ควรเป็นnull
เพื่อตรวจจับปัญหาก่อนหน้าข้อยกเว้นที่เกิดขึ้นเมื่อคุณรู้ว่าในระหว่างการพัฒนาซึ่งวิธีการอาจทำได้ แต่ไม่ควรกลับมาnull
คุณสามารถใช้Debug.Assert()
เพื่อหยุดให้เร็วที่สุดเมื่อมันเกิดขึ้น:
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.
}
แม้ว่าการตรวจสอบนี้จะไม่จบลงในบิลด์ที่วางจำหน่ายของคุณแต่จะทำให้การขว้างNullReferenceException
อีกครั้งเมื่อbook == null
อยู่ในโหมดรันไทม์
GetValueOrDefault()
สำหรับnullable
ประเภทค่าเพื่อระบุค่าเริ่มต้นเมื่อเป็น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 #] หรือIf()
[VB]ชวเลขเพื่อระบุค่าเริ่มต้นเมื่อnull
พบ:
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;
}
?.
หรือ?[x]
สำหรับอาร์เรย์ (มีใน C # 6 และ VB.NET 14):บางครั้งเรียกว่าการนำทางที่ปลอดภัยหรือตัวดำเนินการ Elvis (หลังจากรูปร่าง) หากการแสดงออกทางด้านซ้ายของผู้ประกอบการเป็นโมฆะแล้วทางด้านขวาจะไม่ได้รับการประเมินและกลับเป็นโมฆะแทน นั่นหมายถึงกรณีเช่นนี้:
var title = person.Title.ToUpper();
หากบุคคลนั้นไม่มีชื่อสิ่งนี้จะทำให้เกิดข้อยกเว้นเนื่องจากพยายามโทรหาToUpper
คุณสมบัติที่มีค่า Null
ในC# 5
และด้านล่างสิ่งนี้สามารถป้องกันได้ด้วย:
var title = person.Title == null ? null : person.Title.ToUpper();
ตอนนี้ตัวแปรชื่อจะเป็นโมฆะแทนที่จะโยนข้อยกเว้น C # 6 แนะนำไวยากรณ์ที่สั้นลงสำหรับสิ่งนี้:
var title = person.Title?.ToUpper();
ซึ่งจะส่งผลในการเป็นตัวแปรชื่อnull
และเรียกร้องให้ToUpper
ไม่ได้ทำถ้ามีperson.Title
null
แน่นอนคุณยังต้องตรวจสอบtitle
ค่าว่างหรือใช้ตัวดำเนินการเงื่อนไขว่างร่วมกับตัวดำเนินการรวมค่า null ( ??
) เพื่อระบุค่าเริ่มต้น:
// 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;
เช่นเดียวกันสำหรับอาร์เรย์คุณสามารถใช้?[i]
ดังนี้:
int[] myIntArray=null;
var i=5;
int? elem = myIntArray?[i];
if (!elem.HasValue) Console.WriteLine("No value");
สิ่งนี้จะทำสิ่งต่อไปนี้: ถ้าmyIntArray
เป็นโมฆะนิพจน์จะคืนค่าว่างและคุณสามารถตรวจสอบได้อย่างปลอดภัย ถ้ามันมีอาร์เรย์มันจะทำเช่นเดียวกับ:
elem = myIntArray[i];
และคืนค่าi<sup>th</sup>
องค์ประกอบ
มีการแนะนำในที่นี้C# 8
ประเภทการอ้างอิงบริบทแบบ null และแบบ null สามารถทำการวิเคราะห์แบบสแตติกบนตัวแปรและจัดเตรียมคำเตือนคอมไพเลอร์หากค่าอาจเป็นโมฆะหรือตั้งค่าเป็นโมฆะ ประเภทการอ้างอิง nullable ช่วยให้ประเภทที่จะได้รับอนุญาตอย่างชัดเจนเป็นโมฆะ
บริบทคำอธิบายประกอบที่ nullable และบริบทการเตือน nullable สามารถตั้งค่าสำหรับโครงการโดยใช้Nullable
องค์ประกอบในcsproj
ไฟล์ของคุณ องค์ประกอบนี้กำหนดค่าวิธีที่คอมไพเลอร์ตีความความไร้ค่าของประเภทและคำเตือนใดที่สร้างขึ้น การตั้งค่าที่ถูกต้องคือ:
ชนิดการอ้างอิงแบบ nullable ถูกบันทึกโดยใช้ไวยากรณ์เดียวกับชนิดค่า nullable: a ?
ถูกผนวกเข้ากับชนิดของตัวแปร
C#
รองรับ "iterator blocks" (เรียกว่า "generators" ในภาษายอดนิยมอื่น ๆ ) ข้อยกเว้น dereference Null อาจเป็นเรื่องยากโดยเฉพาะอย่างยิ่งในการดีบักในบล็อกตัววนซ้ำเนื่องจากการดำเนินการที่เลื่อนออกไป:
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) { ... }
หากwhatever
ผลในnull
นั้นMakeFrob
จะโยน ตอนนี้คุณอาจคิดว่าสิ่งที่ถูกต้องคือ:
// 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();
}
ทำไมมันผิด เนื่องจากบล็อกตัววนซ้ำไม่ได้ทำงานจริงจนกว่าforeach
! การเรียกใช้เพื่อGetFrobs
คืนค่าออบเจกต์ซึ่งเมื่อทำซ้ำจะเรียกใช้บล็อกตัววนซ้ำ
โดยการเขียนเช็ค null เช่นนี้คุณป้องกัน dereference null แต่คุณย้ายยกเว้นอาร์กิวเมนต์ null ไปยังจุดที่ซ้ำ , ไม่ถึงจุดของการโทรและที่มากทำให้เกิดความสับสนต่อการแก้ปัญหา
การแก้ไขที่ถูกต้องคือ:
// 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();
}
นั่นคือสร้างเมธอดตัวช่วยส่วนตัวที่มีตัวบล็อกบล็อกตัววนซ้ำและวิธีพื้นผิวสาธารณะที่ใช้การตรวจสอบค่า null และส่งกลับตัววนซ้ำ ตอนนี้เมื่อGetFrobs
มีการเรียกใช้การตรวจสอบค่า null จะเกิดขึ้นทันทีจากนั้นGetFrobsForReal
จะดำเนินการเมื่อมีการวนซ้ำตามลำดับ
หากคุณตรวจสอบแหล่งอ้างอิงสำหรับLINQ
วัตถุคุณจะเห็นว่าเทคนิคนี้ใช้ตลอด มันเป็น clunky มากกว่าการเขียนเล็กน้อย แต่มันทำให้การแก้ไขข้อผิดพลาด nullity ง่ายขึ้นมาก เพิ่มประสิทธิภาพรหัสของคุณเพื่อความสะดวกของผู้โทรที่ไม่สะดวกสบายของผู้เขียน
C#
มีโหมด "ไม่ปลอดภัย" ซึ่งเป็นชื่อที่แสดงถึงเป็นอันตรายอย่างยิ่งเพราะกลไกความปลอดภัยปกติที่ให้ความปลอดภัยของหน่วยความจำและความปลอดภัยของประเภทจะไม่บังคับใช้ คุณไม่ควรจะเขียนโค้ดที่ไม่ปลอดภัยจนกว่าคุณจะมีความเข้าใจอย่างละเอียดและลึกของวิธีการทำงานของหน่วยความจำ
ในโหมดที่ไม่ปลอดภัยคุณควรตระหนักถึงข้อเท็จจริงที่สำคัญสองประการ:
เพื่อให้เข้าใจว่าเหตุใดจึงเป็นเช่นนั้นจะช่วยให้เข้าใจว่า. NET สร้างข้อยกเว้นเป็นโมฆะตั้งแต่แรก (รายละเอียดเหล่านี้ใช้กับ. NET ที่ทำงานบน Windows ระบบปฏิบัติการอื่นใช้กลไกที่คล้ายคลึงกัน)
หน่วยความจำเสมือนจริงในWindows
; แต่ละกระบวนการได้รับพื้นที่หน่วยความจำเสมือนของ "หน้า" จำนวนมากของหน่วยความจำที่ถูกติดตามโดยระบบปฏิบัติการ หน่วยความจำแต่ละหน้ามีการตั้งค่าสถานะไว้ซึ่งจะกำหนดวิธีการใช้งาน: อ่านจากเขียนไปยังดำเนินการและอื่น ๆ ต่ำสุดหน้าจะระบุว่าเป็น "ผลิตข้อผิดพลาดหากเคยใช้ในทางใดทางหนึ่ง"
ทั้งตัวชี้โมฆะและการอ้างอิงโมฆะในC#
นั้นจะแสดงภายในเป็นเลขศูนย์และดังนั้นความพยายามใด ๆ ที่จะตรวจสอบมันลงในหน่วยความจำที่เกี่ยวข้องทำให้ระบบปฏิบัติการสร้างข้อผิดพลาด จากนั้นรันไทม์. NET จะตรวจพบข้อผิดพลาดนี้และเปลี่ยนเป็นข้อยกเว้นการตรวจสอบค่าศูนย์
นั่นเป็นเหตุผลที่การอ้างอิงทั้งตัวชี้โมฆะและการอ้างอิงโมฆะสร้างข้อยกเว้นเดียวกัน
แล้วประเด็นที่สองล่ะ? การอ้างอิงตัวชี้ที่ไม่ถูกต้องใด ๆที่อยู่ในหน้าต่ำสุดของหน่วยความจำเสมือนทำให้เกิดข้อผิดพลาดของระบบปฏิบัติการเดียวกันและดังนั้นจึงเป็นข้อยกเว้นเดียวกัน
ทำไมสิ่งนี้ถึงสมเหตุสมผล? สมมุติว่าเรามีโครงสร้างที่มีสอง ints และตัวชี้ที่ไม่มีการจัดการเท่ากับ null หากเราพยายามยกเลิกการอ้างอิง int ตัวที่สองในโครงสร้างCLR
จะไม่พยายามเข้าถึงที่เก็บข้อมูลที่ตำแหน่งศูนย์ มันจะเข้าถึงที่เก็บข้อมูลในตำแหน่งที่สี่ แต่ในทางตรรกะนี่เป็นข้อผิดพลาดแบบ null เพราะเราไปถึงที่อยู่นั้นผ่านทาง null
หากคุณกำลังทำงานกับรหัสที่ไม่ปลอดภัยและคุณได้รับข้อยกเว้น dereference เป็นโมฆะโปรดทราบว่าตัวชี้ที่ละเมิดไม่จำเป็นต้องเป็นโมฆะ มันสามารถเป็นตำแหน่งใด ๆ ในหน้าต่ำสุดและข้อยกเว้นนี้จะถูกสร้างขึ้น
NullReference Exception
สำหรับVisual Basicไม่แตกต่างจากคนที่อยู่ในC # ท้ายที่สุดพวกเขาทั้งสองรายงานข้อยกเว้นเดียวกันที่กำหนดไว้ใน. NET Framework ซึ่งทั้งคู่ใช้ สาเหตุที่ไม่ซ้ำกับ Visual Basic นั้นเป็นของหายาก
คำตอบนี้จะใช้คำศัพท์ไวยากรณ์และบริบทของ Visual Basic ตัวอย่างที่ใช้มาจากคำถาม Stack Overflow ที่ผ่านมาจำนวนมาก นี่คือการเพิ่มความเกี่ยวข้องโดยใช้ประเภทของสถานการณ์ที่มักจะเห็นในโพสต์ มีคำอธิบายเพิ่มเติมอีกเล็กน้อยสำหรับผู้ที่อาจต้องการ ตัวอย่างที่คล้ายกับของคุณจะมากจดทะเบียนมีแนวโน้มที่นี่
บันทึก:
NullReferenceException
(NRE) วิธีการค้นหาวิธีการแก้ไขและวิธีหลีกเลี่ยง NRE อาจเกิดขึ้นได้หลายวิธีดังนั้นจึงไม่น่าเป็นไปได้ที่คุณจะพบ แต่เพียงผู้เดียวข้อความ"ไม่ได้ตั้งค่าวัตถุเป็นอินสแตนซ์ของวัตถุ"หมายความว่าคุณกำลังพยายามใช้วัตถุที่ยังไม่ได้เริ่มต้น สิ่งนี้ทำให้เดือดลงไปหนึ่งในสิ่งเหล่านี้:
เนื่องจากปัญหาคือการอ้างอิงวัตถุซึ่งก็คือNothing
คำตอบคือการตรวจสอบพวกเขาเพื่อหาที่หนึ่ง จากนั้นกำหนดว่าทำไมจึงไม่เริ่มต้น ถือเมาส์มากกว่าตัวแปรต่างๆและ Visual Studio (VS) จะแสดงค่าของพวกเขา - Nothing
ผู้กระทำผิดจะเป็น
คุณควรลบบล็อค Try / Catch ใด ๆ ออกจากโค้ดที่เกี่ยวข้องโดยเฉพาะอย่างยิ่งบล็อกที่ไม่มีสิ่งใดในบล็อก Catch Nothing
ซึ่งจะทำให้รหัสของคุณจะมีปัญหาเมื่อพยายามที่จะใช้วัตถุซึ่งเป็น นี่คือสิ่งที่คุณต้องการเพราะมันจะระบุที่แน่นอนที่ตั้งของปัญหาและช่วยให้คุณสามารถระบุวัตถุที่ก่อให้เกิดมัน
A MsgBox
ใน Catch ที่แสดงError while...
จะมีความช่วยเหลือเล็กน้อย วิธีนี้ยังนำไปสู่คำถาม Stack Overflow ที่แย่มากเนื่องจากคุณไม่สามารถอธิบายข้อยกเว้นที่แท้จริงวัตถุที่เกี่ยวข้องหรือแม้แต่บรรทัดของรหัสที่มันเกิดขึ้น
คุณยังสามารถใช้Locals Window
( Debug -> Windows -> Locals ) เพื่อตรวจสอบวัตถุของคุณ
เมื่อคุณรู้ว่าปัญหาคืออะไรและที่ไหนมันมักจะง่ายต่อการแก้ไขและเร็วกว่าการโพสต์คำถามใหม่
ดูสิ่งนี้ด้วย:
Dim reg As CashRegister
...
TextBox1.Text = reg.Amount ' NRE
ปัญหาคือDim
ไม่ได้สร้าง CashRegisterวัตถุ ; มันประกาศตัวแปรชื่อreg
ประเภทนั้นเท่านั้น การประกาศตัวแปรวัตถุและการสร้างอินสแตนซ์เป็นสองสิ่งที่แตกต่างกัน
วิธีการรักษา
New
ผู้ประกอบการมักจะสามารถใช้ในการสร้างอินสแตนซ์เมื่อคุณประกาศ:
Dim reg As New CashRegister ' [New] creates instance, invokes the constructor
' Longer, more explicit form:
Dim reg As CashRegister = New CashRegister
เมื่อเหมาะสมที่จะสร้างอินสแตนซ์ในภายหลัง:
Private reg As CashRegister ' Declare
...
reg = New CashRegister() ' Create instance
หมายเหตุ: อย่าใช้Dim
อีกครั้งในขั้นตอนรวมถึงตัวสร้าง (Sub New
):
Private reg As CashRegister
'...
Public Sub New()
'...
Dim reg As New CashRegister
End Sub
สิ่งนี้จะสร้างตัวแปรท้องถิ่นreg
ซึ่งมีอยู่ในบริบทนั้น (ย่อย) เท่านั้น reg
ตัวแปรระดับโมดูลScope
Nothing
ที่คุณจะใช้อย่างอื่นได้ทุกที่ยังคงอยู่
คิดถึง
New
โอเปอเรเตอร์เป็นสาเหตุ # 1 ของการNullReference Exceptions
มองเห็นในคำถามการซ้อนทับที่ตรวจสอบVisual Basic พยายามทำให้กระบวนการชัดเจนซ้ำ ๆ
New
กันโดยใช้: การใช้ตัวNew
ดำเนินการสร้างวัตถุใหม่และการโทรSub New
- ตัวสร้าง - ซึ่งวัตถุของคุณสามารถทำการเริ่มต้นอื่น ๆ
ต้องมีความชัดเจนDim
(หรือPrivate
) เพียงประกาศType
ตัวแปรและ ขอบเขตของตัวแปร - ไม่ว่าจะมีอยู่สำหรับทั้งโมดูล / ชั้นเรียนหรือในท้องถิ่นเพื่อเป็นขั้นตอน - จะถูกกำหนดโดยที่จะประกาศ Private | Friend | Public
กำหนดระดับการเข้าถึงข้อมูลที่ไม่ขอบเขต
สำหรับข้อมูลเพิ่มเติมดู:
อาร์เรย์ต้องเป็นอินสแตนซ์:
Private arr as String()
อาร์เรย์นี้ได้รับการประกาศเท่านั้นไม่ได้ถูกสร้างขึ้น มีหลายวิธีในการเริ่มต้นอาร์เรย์:
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) {}
หมายเหตุ: การเริ่มต้นด้วย VS 2010 เมื่อเริ่มต้นอาร์เรย์ท้องถิ่นโดยใช้ตัวอักษรและOption Infer
ที่As <Type>
และNew
องค์ประกอบเป็นตัวเลือก:
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}
ชนิดข้อมูลและขนาดอาร์เรย์ถูกอนุมานจากข้อมูลที่ได้รับมอบหมาย การประกาศระดับคลาส / โมดูลยังคงต้องการAs <Type>
ด้วยOption Strict
:
Private myDoubles As Double() = {1.5, 2, 9.9, 18, 3.14}
ตัวอย่าง: อาร์เรย์ของวัตถุคลาส
Dim arrFoo(5) As Foo
For i As Integer = 0 To arrFoo.Count - 1
arrFoo(i).Bar = i * 10 ' Exception
Next
มีการสร้างอาร์เรย์ แต่Foo
วัตถุในนั้นไม่มี
วิธีการรักษา
For i As Integer = 0 To arrFoo.Count - 1
arrFoo(i) = New Foo() ' Create Foo instance
arrFoo(i).Bar = i * 10
Next
การใช้ a List(Of T)
จะทำให้เป็นการยากที่จะมีองค์ประกอบโดยไม่มีวัตถุที่ถูกต้อง:
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
สำหรับข้อมูลเพิ่มเติมดู:
. คอลเลกชัน. NET (ซึ่งมีหลายพันธุ์ - รายการ, พจนานุกรม, ฯลฯ ) จะต้องมีการยกตัวอย่างหรือสร้างขึ้น
Private myList As List(Of String)
..
myList.Add("ziggy") ' NullReference
คุณได้รับข้อยกเว้นเดียวกันด้วยเหตุผลเดียวกัน - myList
ถูกประกาศเท่านั้น แต่ไม่มีการสร้างอินสแตนซ์ การรักษาเหมือนกัน:
myList = New List(Of String)
' Or create an instance when declared:
Private myList As New List(Of String)
การกำกับดูแลทั่วไปคือคลาสที่ใช้คอลเล็กชัน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
โพรซีเดอร์ทั้งสองจะส่งผลให้เกิด NRE เนื่องจากbarList
ถูกประกาศเท่านั้นไม่อินสแตนซ์ สร้างตัวอย่างของจะไม่ยังสร้างตัวอย่างของภายในFoo
barList
มันอาจเป็นความตั้งใจที่จะทำสิ่งนี้ในตัวสร้าง:
Public Sub New ' Constructor
' Stuff to do when a new Foo is created...
barList = New List(Of Bar)
End Sub
เหมือนก่อนหน้านี้ไม่ถูกต้อง:
Public Sub New()
' Creates another barList local to this procedure
Dim barList As New List(Of Bar)
End Sub
สำหรับข้อมูลเพิ่มเติมโปรดดูที่ชั้นList(Of T)
ทำงานกับฐานข้อมูลนำเสนอโอกาสมากมายสำหรับ NullReference เพราะอาจมีวัตถุจำนวนมาก ( Command
, Connection
, Transaction
, Dataset
, DataTable
, DataRows
.... ) ในการใช้งานในครั้งเดียว หมายเหตุ:มันไม่สำคัญว่าผู้ให้บริการข้อมูลที่คุณใช้ - MySQL, SQL Server, OleDB และอื่น ๆ - แนวคิดนั้นเหมือนกัน
ตัวอย่างที่ 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
เมื่อก่อนds
วัตถุดาต้าเซ็ตถูกประกาศ แต่ไม่เคยสร้างอินสแตนซ์ DataAdapter
จะกรอกข้อมูลที่มีอยู่DataSet
ไม่ได้สร้าง ในกรณีนี้เนื่องจากds
เป็นตัวแปรโลคัลIDE จะเตือนคุณว่าสิ่งนี้อาจเกิดขึ้น:
เมื่อประกาศว่าเป็นตัวแปรระดับโมดูล / ระดับตามที่ปรากฏเป็นกรณีด้วย con
ตัวแปลภาษาคอมไพเลอร์ไม่สามารถทราบได้ว่าวัตถุนั้นถูกสร้างโดยโพรซีเดอร์อัพสตรีมหรือไม่ อย่าเพิกเฉยต่อคำเตือน
วิธีการรักษา
Dim ds As New DataSet
ตัวอย่างที่ 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)
การพิมพ์ผิดเป็นปัญหาที่นี่: VSEmployees
Employee
ไม่มีDataTable
ชื่อ "พนักงาน" ถูกสร้างขึ้นดังนั้นNullReferenceException
ผลลัพธ์ที่พยายามเข้าถึง ปัญหาที่อาจเกิดขึ้นอีกประการหนึ่งคือสมมติว่าจะไม่มีปัญหาItems
นั้นเมื่อ SQL รวมส่วนคำสั่ง WHERE
วิธีการรักษา
เนื่องจากสิ่งนี้ใช้หนึ่งตารางการใช้Tables(0)
จะหลีกเลี่ยงการสะกดผิด การตรวจสอบRows.Count
ยังสามารถช่วย:
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
เป็นฟังก์ชันที่ส่งคืนจำนวนของRows
ผลกระทบซึ่งสามารถทดสอบได้:
If da.Fill(ds, "Employees") > 0 Then...
ตัวอย่างที่ 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
จะให้TableNames
ตามที่แสดงในตัวอย่างก่อนหน้านี้ แต่ไม่ได้ชื่อแยกจาก SQL หรือฐานข้อมูลตาราง ผลที่ตามมา,ds.Tables("TICKET_RESERVATION")
อ้างอิงตารางที่ไม่มีอยู่
การเยียวยาเหมือนกันให้อ้างอิงตารางตามดัชนี:
If ds.Tables(0).Rows.Count > 0 Then
ดูเพิ่มเติมDataTable ชั้น
If myFoo.Bar.Items IsNot Nothing Then
...
รหัสนี้เป็นเพียงการทดสอบItems
ในขณะที่ทั้งสองอย่างmyFoo
และBar
อาจเป็นอะไรก็ได้ การรักษาคือการทดสอบทั้งห่วงโซ่หรือเส้นทางของวัตถุทีละ:
If (myFoo IsNot Nothing) AndAlso
(myFoo.Bar IsNot Nothing) AndAlso
(myFoo.Bar.Items IsNot Nothing) Then
....
AndAlso
เป็นสิ่งสำคัญ การทดสอบครั้งต่อไปจะไม่ถูกดำเนินการเมื่อFalse
เงื่อนไขแรกเกิดขึ้น วิธีนี้ช่วยให้รหัส 'เจาะ' เข้าสู่วัตถุได้อย่างปลอดภัยในระดับ 'ทีละรายการโดยประเมินmyFoo.Bar
เฉพาะหลังจาก (และถ้า) myFoo
พิจารณาว่าถูกต้อง กลุ่มวัตถุหรือเส้นทางสามารถทำได้ค่อนข้างยาวเมื่อทำการเข้ารหัสวัตถุที่ซับซ้อน:
myBase.myNodes(3).Layer.SubLayer.Foo.Files.Add("somefilename")
ไม่สามารถอ้างอิงอะไรที่ 'ดาวน์สตรีม' ของnull
วัตถุ สิ่งนี้ใช้กับการควบคุมด้วย:
myWebBrowser.Document.GetElementById("formfld1").InnerText = "some value"
ที่นี่myWebBrowser
หรือDocument
อาจจะไม่มีอะไรหรือformfld1
องค์ประกอบอาจไม่มีอยู่
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)
เหนือสิ่งอื่นใดรหัสนี้ไม่คาดว่าผู้ใช้อาจไม่ได้เลือกบางอย่างในการควบคุม UI อย่างน้อยหนึ่งรายการ ListBox1.SelectedItem
อาจจะดีNothing
ดังนั้นListBox1.SelectedItem.ToString
จะส่งผลให้ NRE
วิธีการรักษา
ตรวจสอบข้อมูลก่อนใช้งาน (เช่นใช้Option Strict
และพารามิเตอร์ 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
หรือคุณสามารถใช้ (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
นี่เป็นวิธีที่ใช้กันทั่วไปในการรับ NRE ใน C # ขึ้นอยู่กับว่ามันถูกเข้ารหัสอย่างไร IDE จะรายงานว่าControls
ไม่มีอยู่ในบริบทปัจจุบันหรือ "ไม่สามารถอ้างอิงสมาชิกที่ไม่คงที่" ดังนั้นในระดับหนึ่งนี่เป็นสถานการณ์ VB-only มันก็ซับซ้อนเช่นกันเพราะอาจทำให้เกิดความล้มเหลวได้
อาร์เรย์และคอลเล็กชันไม่สามารถเริ่มต้นได้ด้วยวิธีนี้ รหัสเริ่มต้นนี้จะทำงานก่อนที่จะสร้างสร้างหรือForm
Controls
ผลที่ตามมา:
somevar
มอบหมายจะส่งผลให้ NRE ทันทีเพราะไม่มีสิ่งใดมี.Text
คุณสมบัติการอ้างอิงองค์ประกอบอาร์เรย์ในภายหลังจะส่งผลให้ NRE หากคุณทำเช่นนี้Form_Load
เนื่องจากมีข้อผิดพลาดแปลก ๆ IDE อาจไม่รายงานข้อยกเว้นเมื่อมันเกิดขึ้น ข้อยกเว้นจะปรากฏขึ้นในภายหลังเมื่อรหัสของคุณพยายามใช้อาร์เรย์ "ข้อยกเว้นเงียบ" นี้มีรายละเอียดในโพสต์นี้ สำหรับจุดประสงค์ของเราสิ่งสำคัญคือเมื่อมีบางสิ่งที่ร้ายแรงเกิดขึ้นในขณะที่สร้างแบบฟอร์ม ( Sub New
หรือForm Load
เหตุการณ์) ข้อยกเว้นอาจไม่ได้รับการรายงานรหัสจะออกจากกระบวนการและเพียงแสดงแบบฟอร์ม
เนื่องจากไม่มีรหัสอื่น ๆ ในSub New
หรือForm Load
กิจกรรมของคุณที่จะทำงานหลังจากที่ NRE จึงมีสิ่งอื่น ๆ อีกมากมายที่ไม่สามารถกำหนดค่าเริ่มต้นได้
Sub Form_Load(..._
'...
Dim name As String = NameBoxes(2).Text ' NRE
' ...
' More code (which will likely not be executed)
' ...
End Sub
หมายเหตุสิ่งนี้ใช้กับการอ้างอิงส่วนควบคุมและส่วนประกอบใด ๆ และทั้งหมดซึ่งทำให้สิ่งเหล่านี้ผิดกฎหมาย
Public Class Form1
Private myFiles() As String = Me.OpenFileDialog1.FileName & ...
Private dbcon As String = OpenFileDialog1.FileName & ";Jet Oledb..."
Private studentName As String = TextBox13.Text
ยาบางส่วน
มันเป็นความอยากรู้อยากเห็นว่า VB ไม่ได้ให้คำเตือน แต่วิธีการรักษาคือการประกาศภาชนะที่ระดับรูปแบบ แต่เริ่มต้นพวกเขาในการโหลดแบบฟอร์มการจัดการเหตุการณ์เมื่อควบคุมทำมีอยู่ สิ่งนี้สามารถทำได้Sub New
ตราบใดที่รหัสของคุณอยู่หลังการ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
รหัสอาเรย์อาจไม่ได้ออกมาจากป่า การควบคุมใด ๆ ที่อยู่ในการควบคุมภาชนะ (เช่นGroupBox
หรือPanel
) จะไม่ได้พบในMe.Controls
; พวกเขาจะอยู่ในคอลเลกชันควบคุมของแผงควบคุมหรือ GroupBox จะไม่มีการส่งคืนการควบคุมเมื่อชื่อตัวควบคุมสะกดผิด ( "TeStBox2"
) ในกรณีดังกล่าวNothing
จะถูกเก็บไว้ในองค์ประกอบอาเรย์เหล่านั้นอีกครั้งและ NRE จะส่งผลเมื่อคุณพยายามอ้างอิง
สิ่งเหล่านี้ควรหาได้ง่ายในขณะนี้ที่คุณรู้ว่าคุณกำลังมองหา:
"Button2" ตั้งอยู่บน Panel
วิธีการรักษา
แทนที่จะเป็นการอ้างอิงทางอ้อมด้วยชื่อโดยใช้Controls
คอลเลกชันของแบบฟอร์มให้ใช้การอ้างอิงการควบคุม:
' 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
นี่เป็นกรณีที่ IDE จะเตือนคุณว่า ' ไม่ใช่เส้นทางทั้งหมดที่ส่งคืนค่าและNullReferenceException
อาจส่งผลให้ ' คุณสามารถระงับคำเตือนโดยแทนที่Exit Function
ด้วยReturn Nothing
แต่ไม่สามารถแก้ปัญหาได้ สิ่งใดก็ตามที่พยายามใช้ผลตอบแทนเมื่อsomeCondition = False
จะส่งผลให้เกิด NRE:
bList = myFoo.BarList()
For Each b As Bar in bList ' EXCEPTION
...
วิธีการรักษา
แทนที่ในการทำงานด้วยExit Function
Return bList
ส่งคืนที่ว่างเปล่า ไม่ได้เป็นเช่นเดียวกับการกลับมาList
Nothing
หากมีโอกาสที่วัตถุที่ส่งคืนสามารถNothing
ทดสอบได้ก่อนใช้งาน:
bList = myFoo.BarList()
If bList IsNot Nothing Then...
Try / Catch ที่ติดตั้งไว้ที่ไม่ดีสามารถซ่อนตำแหน่งของปัญหาและส่งผลให้เกิดปัญหาใหม่:
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
นี่เป็นกรณีของวัตถุที่ไม่ได้ถูกสร้างขึ้นตามที่คาดไว้ Catch
แต่ยังแสดงให้เห็นถึงประโยชน์ของเคาน์เตอร์ที่ว่างเปล่า
มีเครื่องหมายจุลภาคพิเศษใน SQL คือ (หลังจาก 'mailAddress') .ExecuteReader
ซึ่งส่งผลให้มีข้อยกเว้นที่ หลังจากที่Catch
ไม่ทำอะไรเลยFinally
พยายามทำความสะอาด แต่เนื่องจากคุณไม่สามารถClose
เป็นDataReader
วัตถุว่างNullReferenceException
เปล่าผลลัพธ์ใหม่ล่าสุด
Catch
บล็อกว่างเปล่าคือสนามเด็กเล่นของปีศาจ OP นี้งงงวยว่าทำไมเขาได้รับ NRE ในFinally
บล็อก ในสถานการณ์อื่น ๆ ที่ว่างเปล่าCatch
อาจส่งผลให้บางสิ่งบางอย่างอื่น ๆ ล่องไปยุ่งเหยิงและทำให้คุณใช้เวลาในการดูสิ่งผิดปกติในสถานที่ที่ไม่ถูกต้องสำหรับปัญหา ("ข้อยกเว้นเงียบ" ที่อธิบายข้างต้นให้ค่าความบันเทิงแบบเดียวกัน)
วิธีการรักษา
อย่าใช้บล็อค Try / Catch ที่ว่างเปล่า - ปล่อยให้รหัสขัดข้องเพื่อให้คุณสามารถก) ระบุสาเหตุ b) ระบุตำแหน่งและ c) ใช้การรักษาที่เหมาะสม บล็อกแบบลอง / จับไม่ได้มีวัตถุประสงค์เพื่อซ่อนข้อยกเว้นจากบุคคลที่มีคุณสมบัติเหมาะสมในการแก้ไขปัญหา - นักพัฒนา
For Each row As DataGridViewRow In dgvPlanning.Rows
If Not IsDBNull(row.Cells(0).Value) Then
...
IsDBNull
ฟังก์ชั่นที่ใช้ในการทดสอบถ้าค่าเท่ากับSystem.DBNull
: จาก MSDN:
กระบวนการ System.DBNull ค่าบ่งชี้ว่าวัตถุแสดงถึงข้อมูลที่ขาดหายไปหรือไม่มีอยู่ DBNull ไม่เหมือนไม่มีอะไรเลยซึ่งบ่งชี้ว่าตัวแปรยังไม่ได้เริ่มต้น
วิธีการรักษา
If row.Cells(0) IsNot Nothing Then ...
ก่อนหน้านี้คุณสามารถทดสอบสิ่งใดก็ได้จากนั้นสำหรับค่าเฉพาะ:
If (row.Cells(0) IsNot Nothing) AndAlso (IsDBNull(row.Cells(0).Value) = False) Then
ตัวอย่างที่ 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
ส่งคืนรายการแรกหรือค่าเริ่มต้นซึ่งเป็นNothing
ประเภทการอ้างอิงและไม่เคยDBNull
:
If getFoo IsNot Nothing Then...
Dim chk As CheckBox
chk = CType(Me.Controls(chkName), CheckBox)
If chk.Checked Then
Return chk
End If
หากไม่พบa CheckBox
ด้วยchkName
(หรือมีอยู่ใน a GroupBox
) chk
จะไม่มีสิ่งใดและพยายามอ้างอิงคุณสมบัติใด ๆ จะส่งผลให้เกิดข้อยกเว้น
วิธีการรักษา
If (chk IsNot Nothing) AndAlso (chk.Checked) Then ...
DGV มีนิสัยใจคอที่เห็นเป็นระยะ ๆ :
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"
ถ้าdgvBooks
มีAutoGenerateColumns = True
มันจะสร้างคอลัมน์ แต่จะไม่ตั้งชื่อดังนั้นรหัสข้างต้นจึงล้มเหลวเมื่อทำการอ้างอิงด้วยชื่อ
วิธีการรักษา
ตั้งชื่อคอลัมน์ด้วยตนเองหรืออ้างอิงโดยดัชนี:
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
เมื่อคุณDataGridView
มีAllowUserToAddRows
เป็นTrue
(ค่าเริ่มต้น) ที่Cells
อยู่ในที่ว่างเปล่า / Nothing
แถวใหม่ที่ด้านล่างทั้งหมดจะประกอบด้วย ความพยายามส่วนใหญ่ในการใช้เนื้อหา (เช่นToString
) จะส่งผลให้ NRE
วิธีการรักษา
ใช้การFor/Each
วนซ้ำและทดสอบIsNewRow
คุณสมบัติเพื่อตรวจสอบว่าเป็นแถวสุดท้ายหรือไม่ ใช้งานAllowUserToAddRows
ได้จริงหรือไม่:
For Each r As DataGridViewRow in myDGV.Rows
If r.IsNewRow = False Then
' ok to use this row
หากคุณใช้การFor n
วนซ้ำให้ปรับเปลี่ยนจำนวนแถวหรือใช้Exit For
เมื่อIsNewRow
เป็นจริง
ในบางสถานการณ์การลองใช้ไอเท็มMy.Settings
ที่StringCollection
สามารถส่งผลให้ NullReference เป็นครั้งแรกที่คุณใช้งาน การแก้ปัญหาเหมือนกัน แต่ไม่ชัดเจน พิจารณา:
My.Settings.FooBars.Add("ziggy") ' foobars is a string collection
เนื่องจาก VB กำลังจัดการการตั้งค่าสำหรับคุณจึงมีเหตุผลที่จะคาดหวังให้เริ่มต้นการรวบรวม มันจะ แต่ถ้าคุณได้เพิ่มรายการเริ่มต้นก่อนหน้าไปยังคอลเลกชัน (ในตัวแก้ไขการตั้งค่า) เนื่องจากคอลเลกชันนั้นเริ่มต้นได้เมื่อมีการเพิ่มรายการจึงยังคงอยู่Nothing
เมื่อไม่มีรายการในเครื่องมือแก้ไขการตั้งค่าที่จะเพิ่ม
วิธีการรักษา
เริ่มต้นการรวบรวมการตั้งค่าในLoad
ตัวจัดการเหตุการณ์ของแบบฟอร์มหาก / เมื่อต้องการ:
If My.Settings.FooBars Is Nothing Then
My.Settings.FooBars = New System.Collections.Specialized.StringCollection
End If
โดยทั่วไปการSettings
รวบรวมจะต้องเริ่มต้นในครั้งแรกที่แอปพลิเคชันทำงานเท่านั้น วิธีการแก้ไขอื่นคือการเพิ่มค่าเริ่มต้นให้กับคอลเลกชันของคุณในโครงการ -> การตั้งค่า | FooBarsบันทึกโครงการแล้วลบค่าปลอม
คุณอาจลืมNew
โอเปอเรเตอร์
หรือ
สิ่งที่คุณคิดว่าจะทำงานได้อย่างไร้ที่ติเพื่อส่งคืนวัตถุที่เริ่มต้นไปยังรหัสของคุณไม่ได้
อย่าเพิกเฉยต่อคำเตือนของคอมไพเลอร์ (เคย) และใช้Option Strict On
(เสมอ)
สถานการณ์ก็คือเมื่อคุณโยนวัตถุโมฆะเป็นประเภทค่า ตัวอย่างเช่นรหัสด้านล่าง:
object o = null;
DateTime d = (DateTime)o;
มันจะส่งNullReferenceException
ต่อไปยังนักแสดง ดูเหมือนว่าจะค่อนข้างชัดเจนในตัวอย่างข้างต้น แต่สิ่งนี้สามารถเกิดขึ้นได้ในสถานการณ์ที่ซับซ้อน "ล่าช้า - ผูกมัด" มากขึ้นซึ่งมีการส่งคืนออบเจ็กต์วัตถุจากรหัสที่คุณไม่ได้เป็นเจ้าของ
ตัวอย่างหนึ่งของสิ่งนี้คือการเชื่อมโยง ASP.NET อย่างง่าย ๆ กับส่วนควบคุมปฏิทิน:
<asp:Calendar runat="server" SelectedDate="<%#Bind("Something")%>" />
นี่SelectedDate
คือในความเป็นจริงทรัพย์สิน - ของDateTime
ประเภท - ของCalendar
ชนิดควบคุมเว็บและมีผลผูกพันที่ดีที่สุดที่จะกลับมาบางสิ่งบางอย่าง null ตัวสร้าง ASP.NET โดยนัยจะสร้างชิ้นส่วนของรหัสที่จะเทียบเท่ากับการส่งรหัสด้านบน และสิ่งนี้จะยกระดับNullReferenceException
ที่ค่อนข้างยากที่จะมองเห็นเพราะมันอยู่ใน ASP.NET สร้างรหัสที่รวบรวมได้ดี ...
DateTime x = (DateTime) o as DateTime? ?? defaultValue;
หมายความว่าตัวแปรในคำถามนั้นไม่ได้ชี้ไปที่อะไรเลย ฉันสามารถสร้างเช่นนี้โดย:
SqlConnection connection = null;
connection.Open();
นั่นจะทำให้เกิดข้อผิดพลาดเพราะในขณะที่ฉันประกาศตัวแปร " connection
" มันไม่ได้ชี้ไปที่อะไรเลย เมื่อฉันพยายามโทรหาสมาชิก " Open
" ไม่มีการอ้างอิงเพื่อแก้ไขและจะทำให้เกิดข้อผิดพลาด
เพื่อหลีกเลี่ยงข้อผิดพลาดนี้:
object == null
หากคุณไม่แน่ใจว่าวัตถุเป็นโมฆะตรวจสอบด้วยเครื่องมือ Resharper ของ JetBrains จะระบุทุกที่ในรหัสของคุณที่มีความเป็นไปได้ของข้อผิดพลาดในการอ้างอิงแบบ null ซึ่งช่วยให้คุณสามารถตรวจสอบ null ได้ ข้อผิดพลาดนี้เป็นแหล่งที่มาของข้อบกพร่องอันดับหนึ่ง IMHO
มันหมายความว่ารหัสของคุณใช้ตัวแปรอ้างอิงวัตถุที่ตั้งค่าเป็นโมฆะ (เช่นมันไม่ได้อ้างอิงอินสแตนซ์ของวัตถุจริง)
เพื่อป้องกันข้อผิดพลาดวัตถุที่อาจเป็นโมฆะควรทดสอบเป็นโมฆะก่อนที่จะถูกนำมาใช้
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
}
โปรดทราบว่าไม่ว่าสถานการณ์จะเป็นสาเหตุหรือไม่ก็ตามใน. NET:
คุณพยายามที่จะใช้ตัวแปรอ้างอิงที่มีค่า/
Nothing
null
เมื่อค่าเป็นNothing
/null
สำหรับตัวแปรอ้างอิงนั่นหมายความว่ามันไม่ได้ถือการอ้างอิงกับอินสแตนซ์ของวัตถุใด ๆ ที่มีอยู่ในฮีปคุณไม่เคยกำหนดบางสิ่งให้กับตัวแปรไม่เคยสร้างอินสแตนซ์ของค่าที่กำหนดให้กับตัวแปรหรือคุณตั้งค่าตัวแปรเท่ากับ
Nothing
/null
ด้วยตนเองหรือคุณเรียกใช้ฟังก์ชันที่ตั้งค่าตัวแปรให้Nothing
/null
สำหรับคุณ
ตัวอย่างของข้อยกเว้นนี้ที่ถูกโยนคือ: เมื่อคุณพยายามตรวจสอบบางสิ่งนั่นเป็นโมฆะ
ตัวอย่างเช่น:
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
}
รันไทม์. NET จะโยน NullReferenceException เมื่อคุณพยายามที่จะดำเนินการกับสิ่งที่ไม่ได้รับอินสแตนซ์นั่นคือรหัสข้างต้น
ในการเปรียบเทียบกับ ArgumentNullException ซึ่งโดยทั่วไปจะถูกโยนเป็นมาตรการป้องกันหากวิธีการที่คาดว่าสิ่งที่จะถูกส่งไปยังมันไม่ได้เป็นโมฆะ
ข้อมูลเพิ่มเติมในC # NullReferenceException และ Null พารามิเตอร์
อัปเดต C # 8.0, 2019: ประเภทการอ้างอิงที่เป็นโมฆะ
C # 8.0 แนะนำประเภทของการอ้างอิง nullableและประเภทของการอ้างอิงที่ไม่ใช่ nullable ประเภทการอ้างอิงดังนั้น nullable เพียงจะต้องตรวจสอบเพื่อหลีกเลี่ยงการNullReferenceException
หากคุณยังไม่ได้เริ่มต้นชนิดการอ้างอิงและคุณต้องการที่จะตั้งหรืออ่านหนึ่งในคุณสมบัติของมันก็จะโยนNullReferenceException
ตัวอย่าง:
Person p = null;
p.Name = "Harry"; // NullReferenceException occurs here.
คุณสามารถหลีกเลี่ยงปัญหานี้ได้โดยตรวจสอบว่าตัวแปรไม่เป็นโมฆะ:
Person p = null;
if (p!=null)
{
p.Name = "Harry"; // Not going to run to this point
}
เพื่อให้เข้าใจอย่างสมบูรณ์ว่าเหตุใดจึงมีการโยน NullReferenceException สิ่งสำคัญคือต้องทราบความแตกต่างระหว่างชนิดของค่าและ [ประเภทอ้างอิง] [3]
ดังนั้นหากคุณกำลังจัดการกับประเภทค่า NullReferenceExceptions ไม่สามารถเกิดขึ้นได้ แม้ว่าคุณจะต้องแจ้งเตือนเมื่อต้องรับมือกับประเภทอ้างอิง !
ประเภทการอ้างอิงเท่านั้นตามชื่อที่แนะนำสามารถระงับการอ้างอิงหรือชี้ไปที่สิ่งใด ๆ (หรือ 'null') ในขณะที่ชนิดของค่ามีค่าอยู่เสมอ
ประเภทการอ้างอิง (ประเภทเหล่านี้ต้องถูกตรวจสอบ):
ประเภทค่า (คุณสามารถละเว้นสิ่งเหล่านี้ได้):
อีกกรณีที่NullReferenceExceptions
สามารถเกิดขึ้นได้คือการใช้ตัว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
ที่นี่Book
และCar
เป็นประเภทที่เข้ากันไม่ได้; Car
ไม่สามารถแปลง / Book
หล่อไป เมื่อหล่อนี้ล้มเหลวผลตอบแทนas
null
ใช้หลังจากนี้ทำให้เกิดmybook
NullReferenceException
โดยทั่วไปคุณควรใช้นักแสดงหรือas
ดังนี้:
หากคุณคาดว่าการแปลงประเภทจะประสบความสำเร็จเสมอ (เช่นคุณรู้ว่าวัตถุควรจะไปข้างหน้าเวลาใด) จากนั้นคุณควรใช้นักแสดง:
ComicBook cb = (ComicBook)specificBook;
หากคุณไม่แน่ใจประเภท แต่คุณต้องการลองใช้เป็นประเภทเฉพาะให้ใช้as
:
ComicBook cb = specificBook as ComicBook;
if (cb != null) {
// ...
}
คุณกำลังใช้วัตถุที่มีการอ้างอิงค่า Null ดังนั้นมันจึงให้ข้อยกเว้นเป็นโมฆะ ในตัวอย่างค่าสตริงเป็นโมฆะและเมื่อตรวจสอบความยาวของมันจะเกิดข้อยกเว้นขึ้น
ตัวอย่าง:
string value = null;
if (value.Length == 0) // <-- Causes exception
{
Console.WriteLine(value); // <-- Never reached
}
ข้อผิดพลาดข้อยกเว้นคือ:
ข้อยกเว้นที่ไม่ได้จัดการ:
System.NullReferenceException: การอ้างอิงวัตถุไม่ได้ตั้งค่าเป็นอินสแตนซ์ของวัตถุ ที่ Program.Main ()
ในขณะที่สิ่งที่ทำให้NullReferenceExceptionsและแนวทางในการหลีกเลี่ยง / แก้ไขข้อยกเว้นดังกล่าวได้รับการแก้ไขในคำตอบอื่น ๆ สิ่งที่โปรแกรมเมอร์หลายคนยังไม่ได้เรียนรู้คือวิธีการดีบักข้อยกเว้นดังกล่าวอย่างอิสระระหว่างการพัฒนา
ใน Visual Studio นี้ขอบคุณมักจะง่ายต่อการดีบัก Visual Studio
ขั้นแรกตรวจสอบให้แน่ใจว่าข้อผิดพลาดที่ถูกต้องจะถูกจับได้ - ดู ฉันจะอนุญาตการแตกหักใน 'System.NullReferenceException' ใน VS2010 ได้อย่างไร หมายเหตุ1
จากนั้นทั้งสองเริ่มต้นด้วยการแก้จุดบกพร่อง (F5)หรือแนบ [ดีบักเกอร์ VS] เพื่อใช้กระบวนการ ในบางครั้งอาจมีประโยชน์ในการใช้Debugger.Break
ซึ่งจะแจ้งให้เปิดตัวดีบั๊ก
ตอนนี้เมื่อมีการโยน NullReferenceException (หรือไม่ได้จัดการ) ตัวดีบักจะหยุด (จำกฎที่ตั้งไว้ข้างต้น?) ในบรรทัดที่มีข้อยกเว้นเกิดขึ้น บางครั้งข้อผิดพลาดจะสังเกตเห็นได้ง่าย
ตัวอย่างเช่นในบรรทัดต่อไปนี้รหัสเท่านั้นที่สามารถทำให้เกิดข้อยกเว้นคือถ้าmyString
ประเมินเป็นโมฆะ นี้สามารถตรวจสอบได้โดยดูที่หน้าต่างดูหรือทำงานนิพจน์ในหน้าต่างทันที
var x = myString.Trim();
ในกรณีที่สูงขึ้นเช่นต่อไปนี้คุณจะต้องใช้หนึ่งในเทคนิคด้านบน (ดูหรือ Windows ทันที) เพื่อตรวจสอบนิพจน์เพื่อตรวจสอบว่านิพจน์str1
นั้นเป็นโมฆะหรือไม่หรือstr2
เป็นโมฆะ
var x = str1.Trim() + str2.Trim();
เมื่อที่ยกเว้นคือโยนได้รับอยู่ก็มักจะเล็กน้อยเพื่อย้อนกลับเหตุผลที่จะหาที่คุ้มค่า null เป็น [ไม่ถูกต้อง] แนะนำ -
ใช้เวลาในการทำความเข้าใจสาเหตุของข้อยกเว้น ตรวจสอบนิพจน์ที่เป็นค่าว่าง ตรวจสอบนิพจน์ก่อนหน้าซึ่งอาจส่งผลให้นิพจน์ null ดังกล่าว เพิ่มเบรกพอยต์และก้าวผ่านโปรแกรมตามความเหมาะสม ใช้ดีบักเกอร์
1 ถ้า Break on Throws ก้าวร้าวเกินไปและตัวดีบักหยุดบน NPE ใน. NET หรือห้องสมุดบุคคลที่สามสามารถใช้Break on User-Unhandledเพื่อ จำกัด ข้อยกเว้นที่จับได้ นอกจากนี้ VS2012 ยังแนะนำJust My Codeซึ่งฉันแนะนำให้เปิดใช้งานด้วย
หากคุณกำลังดีบักด้วย Just My Code ที่เปิดใช้งานลักษณะการทำงานจะแตกต่างกันเล็กน้อย เมื่อเปิดใช้งานรหัสของฉันตัวดีบักเกอร์จะละเว้นข้อยกเว้นภาษาทั่วไปที่เกิดขึ้นครั้งแรก (CLR) ที่ถูกส่งออกนอกรหัสของฉันและไม่ผ่านรหัสของฉัน
object o = null;
DateTime d = (DateTime)o; // NullReferenceException
กรณีที่แกะกล่องแปลง (หล่อ) จาก object
(หรือจากหนึ่งในชั้นเรียนSystem.ValueType
หรือSystem.Enum
หรือจากชนิดของอินเตอร์เฟซ) เพื่อพิมพ์ค่า (นอกเหนือNullable<>
) NullReferenceException
ในตัวเองให้
ในทิศทางอื่น ๆ เป็นมวยแปลงจากNullable<>
ซึ่งมีHasValue
ค่าเท่ากับfalse
การประเภทอ้างอิงสามารถให้การอ้างอิงซึ่งสามารถจากนั้นก็นำไปสู่การnull
NullReferenceException
ตัวอย่างคลาสสิกคือ:
DateTime? d = null;
var s = d.ToString(); // OK, no exception (no boxing), returns ""
var t = d.GetType(); // Bang! d is boxed, NullReferenceException
บางครั้งการชกมวยเกิดขึ้นในอีกทางหนึ่ง ตัวอย่างเช่นวิธีส่วนขยายที่ไม่ใช่แบบทั่วไปนี้:
public static void MyExtension(this object x)
{
x.ToString();
}
รหัสต่อไปนี้จะเป็นปัญหา:
DateTime? d = null;
d.MyExtension(); // Leads to boxing, NullReferenceException occurs inside the body of the called method, not here.
กรณีเหล่านี้เกิดขึ้นเนื่องจากกฎพิเศษที่รันไทม์ใช้เมื่อNullable<>
อินสแตนซ์มวย
การเพิ่มเคสเมื่อชื่อคลาสสำหรับเอนทิตีที่ใช้ในเฟรมเวิร์กเอนทิตีเหมือนกับชื่อคลาสสำหรับไฟล์โค้ด behind เว็บฟอร์ม
สมมติว่าคุณมีเว็บฟอร์ม Contact.aspx ซึ่งคลาส codebehind คือ Contact และคุณมีชื่อเอนทิตีติดต่อ
จากนั้นรหัสต่อไปนี้จะโยน NullReferenceException เมื่อคุณเรียกใช้ context.SaveChanges ()
Contact contact = new Contact { Name = "Abhinav"};
var context = new DataContext();
context.Contacts.Add(contact);
context.SaveChanges(); // NullReferenceException at this line
เพื่อประโยชน์ของ DataContext คลาสที่สมบูรณ์
public class DataContext : DbContext
{
public DbSet<Contact> Contacts {get; set;}
}
และติดต่อชั้นเรียนนิติบุคคล บางครั้งคลาสเอนทิตีเป็นคลาสบางส่วนเพื่อให้คุณสามารถขยายในไฟล์อื่นได้เช่นกัน
public partial class Contact
{
public string Name {get; set;}
}
ข้อผิดพลาดเกิดขึ้นเมื่อทั้งเอนทิตีและคลาส codebehind อยู่ในเนมสเปซเดียวกัน ในการแก้ไขปัญหานี้ให้เปลี่ยนชื่อคลาสเอนทิตีหรือคลาส codebehind สำหรับ Contact.aspx
เหตุผลที่ ฉันยังไม่แน่ใจเกี่ยวกับเหตุผล แต่เมื่อใดก็ตามที่คลาสเอนทิตีจะขยาย System.Web.UI.Page ข้อผิดพลาดนี้เกิดขึ้น
สำหรับการสนทนาให้ดูที่NullReferenceException ใน DbContext.saveChanges ()
กรณีทั่วไปอีกกรณีหนึ่งที่อาจได้รับข้อยกเว้นนี้เกี่ยวข้องกับคลาสที่เยาะเย้ยในระหว่างการทดสอบหน่วย ไม่ว่าคุณจะใช้กรอบการเยาะเย้ยก็ตามคุณต้องทำให้แน่ใจว่าระดับที่เหมาะสมทั้งหมดของลำดับชั้นของคลาสนั้นถูกเยาะเย้ยอย่างเหมาะสม โดยเฉพาะอย่างยิ่งคุณสมบัติทั้งหมดHttpContext
ที่มีการอ้างอิงโดยรหัสภายใต้การทดสอบจะต้องล้อเลียน
ดูที่ " NullReferenceException เกิดขึ้นเมื่อทดสอบ AuthorizationAttribute แบบกำหนดเอง " สำหรับตัวอย่างที่ค่อนข้างละเอียด
ฉันมีมุมมองที่แตกต่างในการตอบคำถามนี้ คำตอบประเภทนี้"ฉันจะทำอะไรได้อีกบ้างเพื่อหลีกเลี่ยงปัญหานี้ "
เมื่อทำงานกับเลเยอร์ที่แตกต่างกันตัวอย่างเช่นในแอปพลิเคชัน MVC ตัวควบคุมต้องการบริการเพื่อเรียกการดำเนินธุรกิจ ในสถานการณ์เช่นการพึ่งพาการฉีดคอนเทนเนอร์สามารถนำมาใช้ในการเริ่มต้นการให้บริการเพื่อหลีกเลี่ยงการNullReferenceException ดังนั้นหมายความว่าคุณไม่จำเป็นต้องกังวลเกี่ยวกับการตรวจสอบหาค่าว่างและเพียงแค่เรียกใช้บริการจากตัวควบคุมราวกับว่ามันจะพร้อมใช้งานเสมอ (และเริ่มต้น) เป็นแบบซิงเกิลหรือต้นแบบ
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();
}
}
ในเรื่องของ"สิ่งที่ฉันควรทำเกี่ยวกับเรื่องนี้"อาจมีคำตอบมากมาย
วิธีที่เป็นทางการ "มากขึ้น" ของการป้องกันเงื่อนไขข้อผิดพลาดในขณะที่การพัฒนากำลังใช้การออกแบบโดยสัญญาในรหัสของคุณ ซึ่งหมายความว่าคุณต้องตั้งค่าคลาสinvariantsและ / หรือแม้แต่เงื่อนไข / ฟังก์ชัน / เมธอดและpostconditionsบนระบบของคุณในขณะที่กำลังพัฒนา
กล่าวโดยสรุปค่าคงที่ของชั้นเรียนทำให้มั่นใจได้ว่าจะมีข้อ จำกัด บางอย่างในชั้นเรียนของคุณที่จะไม่ถูกละเมิดในการใช้งานตามปกติ (และชั้นเรียนจะไม่ได้รับในสถานะที่ไม่สอดคล้องกัน) เงื่อนไขล่วงหน้าหมายความว่าข้อมูลที่ได้รับเป็นอินพุตของฟังก์ชัน / เมธอดต้องเป็นไปตามข้อ จำกัด บางอย่างที่ตั้งไว้และไม่เคยละเมิดและPostconditionsหมายความว่าเอาต์พุตของฟังก์ชัน / เมธอดต้องเป็นไปตามข้อ จำกัด ที่กำหนดอีกครั้งโดยไม่ละเมิด เงื่อนไขสัญญาไม่ควรถูกละเมิดระหว่างการดำเนินการของโปรแกรมที่ปราศจากข้อบกพร่องดังนั้นการออกแบบโดยสัญญาจะถูกตรวจสอบในทางปฏิบัติในโหมดดีบักในขณะที่ถูกปิดการใช้งานในรุ่นเพื่อเพิ่มประสิทธิภาพของระบบที่พัฒนาแล้ว
ด้วยวิธีนี้คุณสามารถหลีกเลี่ยงNullReferenceException
กรณีที่เป็นผลมาจากการละเมิดข้อ จำกัด ที่ตั้งไว้ ตัวอย่างเช่นถ้าคุณใช้คุณสมบัติของวัตถุX
ในคลาสแล้วลองเรียกใช้วิธีการอย่างใดอย่างหนึ่งในภายหลังและX
มีค่า Null สิ่งนี้จะนำไปสู่NullReferenceException
:
public X { get; set; }
public void InvokeX()
{
X.DoSomething(); // if X value is null, you will get a NullReferenceException
}
แต่ถ้าคุณตั้งค่า "property X ต้องไม่มีค่า null" เป็นวิธีการกำหนดเงื่อนไขล่วงหน้าคุณสามารถป้องกันสถานการณ์ที่อธิบายไว้ก่อนหน้านี้:
//Using code contracts:
[ContractInvariantMethod]
protected void ObjectInvariant ()
{
Contract.Invariant ( X != null );
//...
}
ด้วยเหตุนี้โครงการสัญญารหัสอยู่สำหรับโปรแกรมประยุกต์. NET
อีกวิธีหนึ่งคือการออกแบบโดยการทำสัญญาสามารถนำมาประยุกต์ใช้ยืนยัน
UPDATE:มันเป็นมูลค่าการกล่าวขวัญว่าเป็นคำประกาศเกียรติคุณจากเบอร์แทรนด์เมเยอร์ในการเชื่อมต่อกับการออกแบบของเขาในการเขียนโปรแกรมภาษาไอเฟล
A NullReferenceException
ถูกส่งออกมาเมื่อเราพยายามเข้าถึงคุณสมบัติของวัตถุ null หรือเมื่อค่าสตริงว่างเปล่าและเราพยายามเข้าถึงวิธีการสตริง
ตัวอย่างเช่น:
เมื่อเมธอดสตริงของสตริงว่างเข้าถึง:
string str = string.Empty;
str.ToLower(); // throw null reference exception
เมื่อคุณสมบัติของวัตถุ null เข้าถึง:
Public Class Person {
public string Name { get; set; }
}
Person objPerson;
objPerson.Name /// throw Null refernce Exception
String.Empty.ToLower()
จะไม่ส่งข้อยกเว้นการอ้างอิงเป็นโมฆะ เพราะมันหมายถึงสตริงที่แท้จริงแม้ว่าจะเป็นสตริงที่ว่างเปล่า (เช่น""
) เนื่องจากสิ่งนี้มีวัตถุที่จะเรียกToLower()
ใช้จึงไม่เหมาะสมที่จะแสดงข้อยกเว้นการอ้างอิงเป็นศูนย์
TL; DR:ลองใช้Html.Partial
แทนRenderpage
ฉันได้รับObject reference not set to an instance of an object
เมื่อฉันพยายามสร้างมุมมองภายในมุมมองโดยส่งแบบจำลองเช่นนี้:
@{
MyEntity M = new MyEntity();
}
@RenderPage("_MyOtherView.cshtml", M); // error in _MyOtherView, the Model was Null
การดีบักแสดงให้เห็นว่าแบบจำลองนั้นเป็น Null ใน MyOtherView จนกว่าฉันจะเปลี่ยนเป็น:
@{
MyEntity M = new MyEntity();
}
@Html.Partial("_MyOtherView.cshtml", M);
และมันก็ใช้งานได้
นอกจากนี้สาเหตุที่ฉันไม่ต้องHtml.Partial
เริ่มด้วยเพราะบางครั้ง Visual Studio จะพ่นเส้นที่ดูผิดพลาดHtml.Partial
ถ้ามันอยู่ในforeach
วงวนที่สร้างขึ้นแตกต่างกันแม้ว่ามันจะไม่ใช่ข้อผิดพลาดจริง ๆ ก็ตาม:
@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>
แต่ฉันสามารถเรียกใช้แอปพลิเคชันได้โดยไม่มีปัญหากับ "ข้อผิดพลาด" นี้ ฉันสามารถกำจัดข้อผิดพลาดโดยการเปลี่ยนโครงสร้างของforeach
ลูปเป็นดังนี้:
@foreach(var M in MyEntities){
...
}
แม้ว่าฉันจะมีความรู้สึกว่าเป็นเพราะ Visual Studio กำลังอ่านแอมป์แซนด์และวงเล็บ
Html.Partial
ไม่ใช่@Html.Partial
Null
) ดังนั้นฉันรู้ว่าข้อผิดพลาดคือวิธีที่ฉันส่งแบบจำลองมา
คุณทำอะไรได้บ้าง
มีคำตอบที่ดีมากมายที่นี่อธิบายสิ่งที่อ้างอิงโมฆะและวิธีการแก้ปัญหามัน แต่มีน้อยมากเกี่ยวกับวิธีการป้องกันปัญหาหรืออย่างน้อยก็ทำให้ง่ายต่อการจับ
ตรวจสอบข้อโต้แย้ง
ตัวอย่างเช่นวิธีการสามารถตรวจสอบข้อโต้แย้งที่แตกต่างกันเพื่อดูว่าพวกเขาเป็นโมฆะและโยนArgumentNullException
ข้อยกเว้นที่สร้างขึ้นอย่างชัดเจนเพื่อวัตถุประสงค์ที่แน่นอนนี้
คอนสตรัคเตอร์สำหรับArgumentNullException
แม้แต่ใช้ชื่อของพารามิเตอร์และข้อความเป็นอาร์กิวเมนต์เพื่อให้คุณสามารถบอกนักพัฒนาว่าปัญหาคืออะไร
public void DoSomething(MyObject obj) {
if(obj == null)
{
throw new ArgumentNullException("obj", "Need a reference to obj.");
}
}
ใช้เครื่องมือ
นอกจากนี้ยังมีห้องสมุดหลายแห่งที่สามารถช่วยได้ ตัวอย่างเช่น "Resharper" สามารถให้คำเตือนแก่คุณในขณะที่คุณกำลังเขียนโค้ดโดยเฉพาะอย่างยิ่งถ้าคุณใช้คุณลักษณะของพวกเขา: NotNullAttribute
มี "สัญญารหัสไมโครซอฟท์" ที่คุณใช้ไวยากรณ์เหมือนกับContract.Requires(obj != null)
: ซึ่งจะช่วยให้คุณรันไทม์และการตรวจสอบรวบรวมแนะนำสัญญารหัส
นอกจากนี้ยังมี "PostSharp" ซึ่งจะช่วยให้คุณใช้คุณลักษณะเช่นนี้:
public void DoSometing([NotNull] obj)
โดยการทำเช่นนั้นและทำให้ PostSharp เป็นส่วนหนึ่งของกระบวนการสร้างของคุณobj
จะถูกตรวจสอบว่าเป็นโมฆะในขณะใช้งานจริง ดู: ตรวจสอบ PostSharp null
โซลูชั่นรหัสธรรมดา
หรือคุณสามารถเขียนโค้ดแนวทางของคุณเองโดยใช้รหัสเก่าแบบเรียบง่าย ตัวอย่างเช่นที่นี่เป็นโครงสร้างที่คุณสามารถใช้เพื่อตรวจจับการอ้างอิงที่เป็นโมฆะ มันจำลองตามแนวคิดเดียวกันกับ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 };
}
}
คุณจะใช้คล้ายกับแบบเดียวกับที่คุณจะใช้Nullable<T>
ยกเว้นมีเป้าหมายในการประสบความสำเร็จตรงข้าม - null
เพื่อไม่อนุญาตให้ นี่คือตัวอย่างบางส่วน:
NotNull<Person> person = null; // throws exception
NotNull<Person> person = new Person(); // OK
NotNull<Person> person = GetPerson(); // throws exception if GetPerson() returns null
NotNull<T>
ถูกส่งไปยังและจากโดยปริยายเพื่อT
ให้คุณสามารถใช้งานได้ทุกที่ที่คุณต้องการ ตัวอย่างเช่นคุณสามารถส่งPerson
วัตถุไปยังวิธีการที่NotNull<Person>
:
Person person = new Person { Name = "John" };
WriteName(person);
public static void WriteName(NotNull<Person> person)
{
Console.WriteLine(person.Value.Name);
}
ตามที่คุณเห็นด้านบนเช่นเดียวกับ nullable คุณจะเข้าถึงค่าอ้างอิงผ่านValue
คุณสมบัติ หรือมิฉะนั้นคุณสามารถใช้การส่งแบบชัดแจ้งหรือโดยนัยคุณสามารถดูตัวอย่างด้วยค่าส่งคืนด้านล่าง:
Person person = GetPerson();
public static NotNull<Person> GetPerson()
{
return new Person { Name = "John" };
}
หรือคุณสามารถใช้งานได้เมื่อเมธอดเพิ่งส่งคืนT
(ในกรณีนี้Person
) โดยการส่งนักแสดง ตัวอย่างเช่นรหัสต่อไปนี้ต้องการโค้ดด้านบน:
Person person = (NotNull<Person>)GetPerson();
public static Person GetPerson()
{
return new Person { Name = "John" };
}
รวมกับส่วนขยาย
ใช้ร่วมNotNull<T>
กับวิธีการขยายและคุณสามารถครอบคลุมสถานการณ์ได้มากขึ้น นี่คือตัวอย่างของวิธีการขยายที่สามารถมีลักษณะดังนี้:
[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;
}
}
และนี่คือตัวอย่างของวิธีการใช้:
var person = GetPerson().NotNull();
GitHub
สำหรับการอ้างอิงของคุณฉันได้ทำโค้ดข้างต้นบน GitHub คุณสามารถค้นหาได้ที่:
https://github.com/luisperezphd/NotNull
คุณสมบัติภาษาที่เกี่ยวข้อง
C # 6.0 แนะนำ "โอเปอเรเตอร์ที่มีเงื่อนไขเป็นโมฆะ" ซึ่งช่วยได้เล็กน้อย ด้วยคุณสมบัตินี้คุณสามารถอ้างอิงวัตถุซ้อนกันและถ้าหนึ่งของพวกเขาใด ๆ ที่เป็นผลตอบแทนที่แสดงออกทั้งnull
null
สิ่งนี้จะลดจำนวนการตรวจสอบ null ที่คุณต้องทำในบางกรณี ไวยากรณ์คือการใส่เครื่องหมายคำถามหน้าแต่ละจุด ใช้รหัสต่อไปนี้เช่น:
var address = country?.State?.County?.City;
ลองนึกภาพว่าcountry
เป็นวัตถุประเภทCountry
ที่มีคุณสมบัติที่เรียกว่าState
และอื่น ๆ ถ้าcountry
, State
, County
หรือCity
เป็นnull
แล้วaddress will be
null . Therefore you only have to check whether
อยู่is
null`
มันเป็นคุณสมบัติที่ยอดเยี่ยม แต่ให้ข้อมูลน้อยลง มันไม่ได้ทำให้ชัดเจนว่า 4 ใดเป็นโมฆะ
ในตัวเหมือน Nullable?
C # มีชวเลขที่ดีสำหรับNullable<T>
คุณสามารถทำบางสิ่งบางอย่าง nullable int?
โดยการใส่เครื่องหมายคำถามตามประเภทเช่นดังนั้น
มันคงจะดีถ้า C # มีอะไรบางอย่างเช่นNotNull<T>
struct ข้างต้นและมีการจดชวเลขที่คล้ายกันอาจจะเป็นเครื่องหมายอัศเจรีย์เพื่อให้คุณสามารถเขียนสิ่งที่ต้องการ public void WriteName(Person! person)
(!):
น่าสนใจไม่มีคำตอบในหน้านี้ที่กล่าวถึงกรณีขอบทั้งสองหวังว่าไม่มีใครคิดถ้าฉันเพิ่มพวกเขา:
พจนานุกรมทั่วไปใน. NET ไม่ได้เป็นเธรดที่ปลอดภัยและบางครั้งพวกเขาอาจโยน a NullReference
หรือ (บ่อยครั้งมากขึ้น) KeyNotFoundException
เมื่อคุณพยายามเข้าถึงคีย์จากสองเธรดที่เกิดขึ้นพร้อมกัน ข้อยกเว้นค่อนข้างทำให้เข้าใจผิดในกรณีนี้
ถ้า a NullReferenceException
ถูกขว้างด้วยunsafe
รหัสคุณอาจดูตัวแปรพอยน์เตอร์ของคุณและตรวจสอบIntPtr.Zero
สิ่งนั้น ซึ่งเป็นสิ่งเดียวกัน ("ข้อยกเว้นตัวชี้โมฆะ") แต่ในรหัสที่ไม่ปลอดภัยตัวแปรมักถูกส่งไปยังประเภท / อาร์เรย์มูลค่า ฯลฯ และคุณกระแทกหัวของคุณกับผนัง ข้อยกเว้น
(อีกสาเหตุสำหรับการไม่ใช้รหัสที่ไม่ปลอดภัยเว้นแต่คุณต้องการโดยวิธี)
null
ที่ใด?
คุณสามารถแก้ไข NullReferenceException ได้อย่างหมดจดโดยใช้ตัวดำเนินการ Null แบบมีเงื่อนไขใน c # 6 และเขียนรหัสน้อยลงเพื่อจัดการกับการตรวจสอบที่ว่างเปล่า
มันถูกใช้เพื่อทดสอบเป็นโมฆะก่อนที่จะทำการเข้าถึงสมาชิก (?.) หรือดัชนี (? [)
ตัวอย่าง
var name = p?.Spouse?.FirstName;
เทียบเท่ากับ:
if (p != null)
{
if (p.Spouse != null)
{
name = p.Spouse.FirstName;
}
}
ผลลัพธ์คือชื่อจะเป็นโมฆะเมื่อ p เป็นโมฆะหรือเมื่อ p.Spouse เป็นโมฆะ
มิฉะนั้นชื่อตัวแปรจะถูกกำหนดค่าของ p.Spouse.FirstName
สำหรับรายละเอียดเพิ่มเติม: ผู้ประกอบการที่มีเงื่อนไข
บรรทัดข้อผิดพลาด "การอ้างอิงวัตถุไม่ได้ถูกตั้งค่าเป็นอินสแตนซ์ของวัตถุ" ระบุว่าคุณไม่ได้กำหนดวัตถุอินสแตนซ์ให้กับการอ้างอิงวัตถุและคุณยังคงเข้าถึงคุณสมบัติ / วิธีการของวัตถุนั้น
ตัวอย่างเช่นสมมติว่าคุณมีคลาสชื่อ myClass และมันมีหนึ่งคุณสมบัติ prop1
public Class myClass
{
public int prop1 {get;set;}
}
ตอนนี้คุณกำลังเข้าถึง prop1 นี้ในคลาสอื่น ๆ เช่นเดียวกับด้านล่าง:
public class Demo
{
public void testMethod()
{
myClass ref = null;
ref.prop1 = 1; //This line throws error
}
}
บรรทัดด้านบนพ่นข้อผิดพลาดเนื่องจากการอ้างอิงของคลาส myClass ถูกประกาศ แต่ไม่อินสแตนซ์หรือไม่ได้กำหนดอินสแตนซ์ของวัตถุให้กับการอ้างอิงของคลาสนั้น
ในการแก้ไขปัญหานี้คุณต้องสร้างอินสแตนซ์ (กำหนดวัตถุให้กับการอ้างอิงของคลาสนั้น)
public class Demo
{
public void testMethod()
{
myClass ref = null;
ref = new myClass();
ref.prop1 = 1;
}
}
NullReferenceException หรือการอ้างอิงวัตถุไม่ได้ถูกตั้งค่าเป็นอินสแตนซ์ของวัตถุที่เกิดขึ้นเมื่อวัตถุของคลาสที่คุณพยายามจะใช้ไม่ได้สร้างอินสแตนซ์ ตัวอย่างเช่น:
สมมติว่าคุณมีชั้นเรียนชื่อนักเรียน
public class Student
{
private string FirstName;
private string LastName;
public string GetFullName()
{
return FirstName + LastName;
}
}
ตอนนี้ให้พิจารณาอีกชั้นหนึ่งที่คุณพยายามดึงชื่อเต็มของนักเรียน
public class StudentInfo
{
public string GetStudentName()
{
Student s;
string fullname = s.GetFullName();
return fullname;
}
}
ดังที่เห็นในรหัสข้างต้นคำสั่งของ นักเรียน - เพียงประกาศตัวแปรประเภทนักเรียนโปรดทราบว่าชั้นเรียนของนักเรียนไม่ได้ยกตัวอย่างทันที ณ จุดนี้ ดังนั้นเมื่อคำสั่งs.GetFullName ()ได้รับการดำเนินการก็จะโยน NullReferenceException
ในแง่ง่าย:
คุณกำลังพยายามเข้าถึงวัตถุที่ไม่ได้สร้างขึ้นหรือไม่อยู่ในหน่วยความจำในปัจจุบัน
ดังนั้นวิธีแก้ไขปัญหานี้:
ดีบั๊กและปล่อยให้ตัวดีบักหยุดทำงาน ... มันจะนำคุณไปยังตัวแปรที่ถูกทำลาย ... ตอนนี้งานของคุณคือการแก้ไขสิ่งนี้ .. การใช้คำค้นหาใหม่ในที่ที่เหมาะสม
ถ้ามันเกิดจากคำสั่งฐานข้อมูลบางอย่างเพราะวัตถุไม่ได้อยู่แล้วสิ่งที่คุณต้องทำคือการตรวจสอบเป็นโมฆะและจัดการกับมัน:
if (i == null) {
// Handle this
}
สิ่งที่ยากที่สุด .. หากGCเก็บรวบรวมวัตถุอยู่แล้ว ... โดยทั่วไปจะเกิดขึ้นหากคุณพยายามค้นหาวัตถุโดยใช้สตริง ... นั่นคือการค้นหาโดยใช้ชื่อของวัตถุก็อาจเกิดขึ้นได้ว่า GC อาจมีอยู่แล้ว ล้างมัน ... นี่หายากและจะกลายเป็นปัญหา ... วิธีที่ดีกว่าในการจัดการกับปัญหานี้คือทำการตรวจสอบ null ในทุกที่ที่จำเป็นในระหว่างกระบวนการพัฒนา สิ่งนี้จะช่วยคุณประหยัดเวลาได้มาก
โดยการค้นหาตามชื่อฉันหมายถึงกรอบบางอย่างช่วยให้คุณสามารถ FIndObjects โดยใช้สตริงและรหัสอาจมีลักษณะเช่นนี้: FindObject ("ObjectName");
แท้จริงวิธีที่ง่ายที่สุดในการแก้ไข NullReferenceExeption มีสองวิธี หากคุณมี GameObject เช่นแนบสคริปต์และตัวแปรชื่อ rb (rigidbody) ตัวแปรนี้จะเริ่มเป็นโมฆะเมื่อคุณเริ่มเกม
นี่คือเหตุผลที่คุณได้รับ NullReferenceExeption เนื่องจากคอมพิวเตอร์ไม่มีข้อมูลที่เก็บอยู่ในตัวแปรนั้น
ฉันจะใช้ตัวแปร RigidBody เป็นตัวอย่าง
เราสามารถเพิ่มข้อมูลได้อย่างง่ายดายจริงๆในสองสามวิธี:
rb = GetComponent<Rigidbody>();
Start()
หรือAwake()
หน้าที่ของคุณ rb = AddComponent<RigidBody>();
หมายเหตุเพิ่มเติม: หากคุณต้องการความสามัคคีในการเพิ่มองค์ประกอบให้กับวัตถุของคุณและคุณอาจลืมที่จะเพิ่มองค์ประกอบหนึ่งคุณสามารถพิมพ์ข้อความ[RequireComponent(typeof(RigidBody))]
ประกาศของชั้นเรียนได้ (พื้นที่ด้านล่างการใช้ทั้งหมดของคุณ)
สนุกและมีเกมทำสนุก!
หากเราพิจารณาสถานการณ์ทั่วไปที่สามารถโยนข้อยกเว้นนี้ได้การเข้าถึงคุณสมบัติด้วยวัตถุที่ด้านบน
Ex:
string postalcode=Customer.Address.PostalCode;
//if customer or address is null , this will through exeption
ในที่นี่หากที่อยู่เป็นโมฆะคุณจะได้รับ NullReferenceException
ดังนั้นในทางปฏิบัติเราควรใช้การตรวจสอบโมฆะก่อนเข้าถึงคุณสมบัติในวัตถุดังกล่าว
string postalcode=Customer?.Address?.PostalCode;
//if customer or address is null , this will return null, without through a exception
นี้เป็นพื้นเป็นข้อยกเว้นที่อ้างอิง Null ขณะที่ไมโครซอฟท์ states-
ข้อยกเว้น NullReferenceException ถูกส่งออกมาเมื่อคุณพยายามเข้าถึงสมาชิกประเภทที่มีค่าเป็น null
นั่นหมายความว่าหากสมาชิกคนใดที่ไม่มีคุณค่าใด ๆ และเรากำลังทำให้สมาชิกคนนั้นปฏิบัติภารกิจบางอย่างระบบจะโยนข้อความและพูดอย่างไม่ต้องสงสัย
“ เฮ้เดี๋ยวก่อนสมาชิกคนนั้นไม่มีค่าดังนั้นมันจึงไม่สามารถทำงานที่คุณส่งมอบได้”
ข้อยกเว้นระบุว่ามีบางสิ่งที่กำลังถูกเรียก แต่ไม่มีการตั้งค่า ดังนั้นสิ่งนี้แสดงว่ามันเกิดขึ้นเฉพาะในขณะที่ใช้ประเภทการอ้างอิงเป็นประเภทค่าที่ไม่เป็นโมฆะ
NullReferenceException จะไม่เกิดขึ้นหากเราใช้สมาชิกประเภทค่า
class Program
{
static void Main(string[] args)
{
string str = null;
Console.WriteLine(str.Length);
Console.ReadLine();
}
}
สตริงง่ายรหัสที่แสดงให้เห็นข้างต้นที่ได้รับมอบหมายด้วยnullค่า
ตอนนี้เมื่อฉันพยายามที่จะพิมพ์ความยาวของสตริงstrฉันจะได้รับข้อยกเว้นที่ไม่สามารถจัดการได้ของข้อความประเภท 'System.NullReferenceException'เนื่องจากสมาชิกstrชี้ไปที่ null และไม่มีความยาวของโมฆะใด ๆ
' NullReferenceException ' ก็เกิดขึ้นเมื่อเราลืมสร้างอินสแตนซ์ของประเภทอ้างอิง
สมมติว่าฉันมีคลาสและวิธีสมาชิกในนั้น ฉันไม่ได้ยกระดับชั้นเรียนของฉัน แต่ตั้งชื่อชั้นเรียนของฉันเท่านั้น ตอนนี้ถ้าฉันพยายามใช้วิธีคอมไพเลอร์จะโยนข้อผิดพลาดหรือออกคำเตือน (ขึ้นอยู่กับคอมไพเลอร์)
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");
}
}
คอมไพเลอร์สำหรับโค้ดด้านบนทำให้เกิดข้อผิดพลาดที่ตัวแปรobjไม่ได้ถูกกำหนดซึ่งหมายความว่าตัวแปรของเรามีค่า Null หรือไม่มีอะไรเลย คอมไพเลอร์สำหรับโค้ดด้านบนทำให้เกิดข้อผิดพลาดที่ตัวแปรobjไม่ได้ถูกกำหนดซึ่งหมายความว่าตัวแปรของเรามีค่า Null หรือไม่มีอะไรเลย
NullReferenceException เกิดขึ้นเนื่องจากความผิดของเราที่ไม่ได้ตรวจสอบค่าของวัตถุ เรามักจะปล่อยให้ค่าวัตถุไม่ถูกตรวจสอบในการพัฒนารหัส
มันเกิดขึ้นเมื่อเราลืมยกตัวอย่างวัตถุของเรา การใช้วิธีการคุณสมบัติคอลเลกชัน ฯลฯ ซึ่งสามารถส่งคืนหรือตั้งค่า Null ได้ก็อาจเป็นสาเหตุของข้อยกเว้นนี้
มีหลายวิธีและวิธีการหลีกเลี่ยงข้อยกเว้นที่มีชื่อเสียงนี้:
การตรวจสอบอย่างชัดเจน: เราควรปฏิบัติตามประเพณีในการตรวจสอบวัตถุคุณสมบัติวิธีการอาร์เรย์และคอลเลกชันไม่ว่าจะเป็นโมฆะ สิ่งนี้สามารถนำไปใช้งานได้ง่ายโดยใช้ข้อความสั่งเงื่อนไขเช่น if-else if-else เป็นต้น
การจัดการข้อยกเว้น: หนึ่งในวิธีสำคัญในการจัดการข้อยกเว้นนี้ การใช้บล็อก try-catch-catch ในที่สุดเราสามารถควบคุมข้อยกเว้นนี้และเก็บบันทึกของมันได้ สิ่งนี้มีประโยชน์มากเมื่อใบสมัครของคุณอยู่ในขั้นตอนการผลิต
ตัวดำเนินการ Null: ตัวดำเนินการ Null Coalescing และตัวดำเนินการเงื่อนไข Null สามารถใช้ประโยชน์ได้ในขณะที่ตั้งค่าให้กับวัตถุตัวแปรคุณสมบัติและเขตข้อมูล
ดีบักเกอร์: สำหรับนักพัฒนาเรามีอาวุธขนาดใหญ่สำหรับการดีบักกับเรา ถ้าหากเราเผชิญกับ NullReferenceException ระหว่างการพัฒนาเราสามารถใช้ดีบักเกอร์เพื่อไปยังแหล่งที่มาของข้อยกเว้น
วิธีที่สร้างขึ้น: วิธีการของระบบเช่น GetValueOrDefault (), IsNullOrWhiteSpace () และ IsNullorEmpty () ตรวจสอบ nulls และกำหนดค่าเริ่มต้นหากมีค่า null
มีคำตอบที่ดีอยู่แล้วที่นี่แล้ว นอกจากนี้คุณยังสามารถตรวจสอบรายละเอียดเพิ่มเติมเกี่ยวกับตัวอย่างบนของบล็อก
หวังว่านี่จะช่วยได้เช่นกัน!
หากมีใครได้รับข้อความนี้ระหว่างการบันทึกหรือการคอมไพล์บิลด์ให้ปิดไฟล์ทั้งหมดแล้วเปิดไฟล์ใด ๆ เพื่อคอมไพล์และบันทึก
สำหรับฉันเหตุผลคือฉันได้เปลี่ยนชื่อไฟล์และไฟล์เก่าก็ยังคงเปิดอยู่