ฉันมีโค้ดบางส่วนและเมื่อมันประมวลผลมันจะพ่น 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 โปรดทราบว่าการเข้าถึงผ่านทางพ่นแทนถ้าเป็นaa.ValueInvalidOperationExceptionNullReferenceExceptionanull- คุณควรจะตรวจสอบก่อนเช่นถ้าคุณมีอีกตัวแปรใน 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()ในชี้อ้างอิงถึงstringnull
คุณจะหาที่มาของ 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นี่
asvar 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.Titlenull
แน่นอนคุณยังต้องตรวจสอบ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ตัวแปรระดับโมดูลScopeNothingที่คุณจะใช้อย่างอื่นได้ทุกที่ยังคงอยู่
คิดถึง
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 IfFillเป็นฟังก์ชันที่ส่งคืนจำนวนของ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 ThenDataAdapterจะให้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 = TruexlWorkSheet = 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:
คุณพยายามที่จะใช้ตัวแปรอ้างอิงที่มีค่า/
Nothingnullเมื่อค่าเป็น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ใช้หลังจากนี้ทำให้เกิดmybookNullReferenceException
โดยทั่วไปคุณควรใช้นักแสดงหรือ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 nullNotNull<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 แนะนำ "โอเปอเรเตอร์ที่มีเงื่อนไขเป็นโมฆะ" ซึ่งช่วยได้เล็กน้อย ด้วยคุณสมบัตินี้คุณสามารถอ้างอิงวัตถุซ้อนกันและถ้าหนึ่งของพวกเขาใด ๆ ที่เป็นผลตอบแทนที่แสดงออกทั้งnullnull
สิ่งนี้จะลดจำนวนการตรวจสอบ null ที่คุณต้องทำในบางกรณี ไวยากรณ์คือการใส่เครื่องหมายคำถามหน้าแต่ละจุด ใช้รหัสต่อไปนี้เช่น:
var address = country?.State?.County?.City;ลองนึกภาพว่าcountryเป็นวัตถุประเภทCountryที่มีคุณสมบัติที่เรียกว่าStateและอื่น ๆ ถ้าcountry, State, CountyหรือCityเป็นnullแล้วaddress will benull . Therefore you only have to check whetherอยู่isnull`
มันเป็นคุณสมบัติที่ยอดเยี่ยม แต่ให้ข้อมูลน้อยลง มันไม่ได้ทำให้ชัดเจนว่า 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
มีคำตอบที่ดีอยู่แล้วที่นี่แล้ว นอกจากนี้คุณยังสามารถตรวจสอบรายละเอียดเพิ่มเติมเกี่ยวกับตัวอย่างบนของบล็อก
หวังว่านี่จะช่วยได้เช่นกัน!
หากมีใครได้รับข้อความนี้ระหว่างการบันทึกหรือการคอมไพล์บิลด์ให้ปิดไฟล์ทั้งหมดแล้วเปิดไฟล์ใด ๆ เพื่อคอมไพล์และบันทึก
สำหรับฉันเหตุผลคือฉันได้เปลี่ยนชื่อไฟล์และไฟล์เก่าก็ยังคงเปิดอยู่