NullReferenceException คืออะไรและฉันจะแก้ไขได้อย่างไร


1875

ฉันมีโค้ดบางส่วนและเมื่อมันประมวลผลมันจะพ่น a NullReferenceExceptionโดยพูดว่า:

การอ้างอิงวัตถุไม่ได้ถูกตั้งค่าเป็นอินสแตนซ์ของวัตถุ

สิ่งนี้หมายความว่าอะไรและฉันจะทำอย่างไรเพื่อแก้ไขข้อผิดพลาดนี้


ผู้ช่วยยกเว้นใน VS 2017 จะเป็นประโยชน์มากขึ้นในการวินิจฉัยสาเหตุของข้อยกเว้นนี้ - blogs.msdn.microsoft.com/visualstudio/2016/11/28/...ภายใต้ข้อยกเว้นตัวช่วยใหม่
Zev Spitz

ผู้เข้าชมในอนาคตเรียนตอบคำถามนี้อย่างเท่าเทียมกันใช้กับArgumentNullException หากคำถามของคุณถูกปิดซ้ำกับคำถามนี้และคุณประสบปัญหากับ ANE โปรดทำตามคำแนะนำในการแก้ปัญหาและแก้ไขปัญหาของคุณ

@ จะ ANE เกิดขึ้นได้ก็ต่อเมื่อมีการส่งค่า null เป็นพารามิเตอร์ คุณยกตัวอย่างได้ไหมถ้าคำถาม ANE ปิดซ้ำกับคำถามนี้?
John Saunders

มันมากับ Meta แต่ฉันต้องขุดหาลิงก์ แต่สำหรับความคิดเห็นนั้น ANE เป็นเพียงแค่ NRE แต่มีคนเพิ่มการตรวจสอบการยึดเอาเสียก่อนและอย่างน้อยคุณก็รู้ว่าสิ่งใดเป็นโมฆะ (ชื่ออาร์กิวเมนต์มีให้) ดังนั้นจึงง่ายต่อการวินิจฉัยมากกว่า NRE

คำตอบ:


2415

สาเหตุคืออะไร?

บรรทัดล่าง

คุณกำลังพยายามใช้สิ่งที่null(หรือNothingใน VB.NET) ซึ่งหมายความว่าคุณสามารถตั้งค่าเป็นnullหรือคุณไม่เคยกำหนดเป็นอะไรเลย

เหมือนอะไรก็ได้nullผ่านไป หากอยู่null ในวิธี "A" อาจเป็นได้ว่าวิธี "B" ส่งผ่านnull ไปยังวิธี "A"

null สามารถมีความหมายต่างกัน:

    1. ตัวแปรออบเจคต์ซึ่งไม่ได้กำหนดค่าเริ่มต้นและดังนั้นจึงไม่มีประโยชน์ NullReferenceExceptionในกรณีนี้ถ้าคุณเข้าถึงคุณสมบัติหรือวิธีการของวัตถุดังกล่าวจะทำให้เกิดการ
    1. ผู้พัฒนาใช้nullความตั้งใจในการระบุว่าไม่มีคุณค่าที่มีความหมาย โปรดทราบว่า C # มีแนวคิดของประเภทข้อมูลที่เป็นโมฆะสำหรับตัวแปร (เช่นตารางฐานข้อมูลสามารถมีเขตข้อมูลที่เป็นโมฆะได้) - คุณสามารถกำหนดให้nullกับพวกเขาเพื่อระบุว่าไม่มีค่าที่เก็บอยู่ในนั้นตัวอย่างเช่นint? a = null;เครื่องหมายคำถามระบุว่า aตัวแปร คุณสามารถตรวจสอบว่าทั้งที่มีหรือif (a.HasValue) {...} if (a==null) {...}ตัวแปร Nullable เช่นaตัวอย่างนี้จะช่วยให้การเข้าถึงค่าผ่านทางอย่างชัดเจนหรือเพียงแค่เป็นปกติผ่านทางa.Value โปรดทราบว่าการเข้าถึงผ่านทางพ่นแทนถ้าเป็นa
      a.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.

Jagged Arays

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;

วงจรชีวิตของเพจ ASP.NET:

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!";
    }
}

ค่าเซสชันของ ASP.NET

// if the "FirstName" session value has not yet been set,
// then this line will throw a NullReferenceException
string firstName = Session["FirstName"].ToString();

ASP.NET MVC รุ่นมุมมองที่ว่างเปล่า

หากข้อยกเว้นเกิดขึ้นเมื่ออ้างอิงคุณสมบัติของ@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

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ตัวเองเป็นโมฆะ) ดังนั้นจงระวังให้ดี

LINQ 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):

มีการแนะนำในที่นี้C# 8ประเภทการอ้างอิงบริบทแบบ null และแบบ null สามารถทำการวิเคราะห์แบบสแตติกบนตัวแปรและจัดเตรียมคำเตือนคอมไพเลอร์หากค่าอาจเป็นโมฆะหรือตั้งค่าเป็นโมฆะ ประเภทการอ้างอิง nullable ช่วยให้ประเภทที่จะได้รับอนุญาตอย่างชัดเจนเป็นโมฆะ

บริบทคำอธิบายประกอบที่ nullable และบริบทการเตือน nullable สามารถตั้งค่าสำหรับโครงการโดยใช้Nullableองค์ประกอบในcsprojไฟล์ของคุณ องค์ประกอบนี้กำหนดค่าวิธีที่คอมไพเลอร์ตีความความไร้ค่าของประเภทและคำเตือนใดที่สร้างขึ้น การตั้งค่าที่ถูกต้องคือ:

  • เปิดใช้งาน: บริบทคำอธิบายประกอบที่ nullable ถูกเปิดใช้งาน บริบทคำเตือนที่ nullable ถูกเปิดใช้งาน ตัวแปรประเภทการอ้างอิงสตริงตัวอย่างเช่นไม่สามารถเป็นโมฆะ คำเตือนความถูกต้องทั้งหมดถูกเปิดใช้งาน
  • ปิดใช้งาน: บริบทคำอธิบายประกอบที่ nullable ถูกปิดใช้งาน บริบทคำเตือนที่ nullable ถูกปิดใช้งาน ตัวแปรประเภทการอ้างอิงมีความหลงลืมเหมือนกับรุ่นก่อนหน้าของ C # คำเตือนความถูกต้องทั้งหมดถูกปิดใช้งาน
  • อย่างปลอดภัย: เปิดใช้งานบริบทคำอธิบายประกอบที่เป็นโมฆะ บริบทคำเตือนที่สามารถ nullable ได้อย่างปลอดภัย ตัวแปรของประเภทการอ้างอิงไม่เป็นโมฆะ คำเตือนความปลอดภัยเป็นโมฆะทั้งหมดถูกเปิดใช้งาน
  • คำเตือน: บริบทคำอธิบายประกอบที่ nullable ถูกปิดใช้งาน บริบทคำเตือนที่ nullable ถูกเปิดใช้งาน ตัวแปรประเภทการอ้างอิงนั้นจะไม่สนใจ คำเตือนความถูกต้องทั้งหมดถูกเปิดใช้งาน
  • safeonlywarnings: บริบทคำอธิบายประกอบที่ nullable ถูกปิดใช้งาน บริบทคำเตือนที่สามารถ nullable ได้อย่างปลอดภัย ตัวแปรประเภทการอ้างอิงนั้นจะไม่สนใจ คำเตือนความปลอดภัยเป็นโมฆะทั้งหมดถูกเปิดใช้งาน

ชนิดการอ้างอิงแบบ nullable ถูกบันทึกโดยใช้ไวยากรณ์เดียวกับชนิดค่า nullable: a ?ถูกผนวกเข้ากับชนิดของตัวแปร

เทคนิคพิเศษสำหรับการแก้ไขข้อบกพร่องและการแก้ไข derefs null ในตัววนซ้ำ

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 ง่ายขึ้นมาก เพิ่มประสิทธิภาพรหัสของคุณเพื่อความสะดวกของผู้โทรที่ไม่สะดวกสบายของผู้เขียน

บันทึกย่อเกี่ยวกับการกำหนดค่า Null แบบไม่ปลอดภัยในรหัส

C#มีโหมด "ไม่ปลอดภัย" ซึ่งเป็นชื่อที่แสดงถึงเป็นอันตรายอย่างยิ่งเพราะกลไกความปลอดภัยปกติที่ให้ความปลอดภัยของหน่วยความจำและความปลอดภัยของประเภทจะไม่บังคับใช้ คุณไม่ควรจะเขียนโค้ดที่ไม่ปลอดภัยจนกว่าคุณจะมีความเข้าใจอย่างละเอียดและลึกของวิธีการทำงานของหน่วยความจำ

ในโหมดที่ไม่ปลอดภัยคุณควรตระหนักถึงข้อเท็จจริงที่สำคัญสองประการ:

  • การยกเลิกการอ้างอิงตัวชี้ null จะสร้างข้อยกเว้นเช่นเดียวกับการอ้างอิงการอ้างอิง null
  • การพิจารณาตัวชี้ที่ไม่เป็นนัลไม่ถูกต้องสามารถสร้างข้อยกเว้นนั้นได้ในบางสถานการณ์

เพื่อให้เข้าใจว่าเหตุใดจึงเป็นเช่นนั้นจะช่วยให้เข้าใจว่า. NET สร้างข้อยกเว้นเป็นโมฆะตั้งแต่แรก (รายละเอียดเหล่านี้ใช้กับ. NET ที่ทำงานบน Windows ระบบปฏิบัติการอื่นใช้กลไกที่คล้ายคลึงกัน)

หน่วยความจำเสมือนจริงในWindows; แต่ละกระบวนการได้รับพื้นที่หน่วยความจำเสมือนของ "หน้า" จำนวนมากของหน่วยความจำที่ถูกติดตามโดยระบบปฏิบัติการ หน่วยความจำแต่ละหน้ามีการตั้งค่าสถานะไว้ซึ่งจะกำหนดวิธีการใช้งาน: อ่านจากเขียนไปยังดำเนินการและอื่น ๆ ต่ำสุดหน้าจะระบุว่าเป็น "ผลิตข้อผิดพลาดหากเคยใช้ในทางใดทางหนึ่ง"

ทั้งตัวชี้โมฆะและการอ้างอิงโมฆะในC#นั้นจะแสดงภายในเป็นเลขศูนย์และดังนั้นความพยายามใด ๆ ที่จะตรวจสอบมันลงในหน่วยความจำที่เกี่ยวข้องทำให้ระบบปฏิบัติการสร้างข้อผิดพลาด จากนั้นรันไทม์. NET จะตรวจพบข้อผิดพลาดนี้และเปลี่ยนเป็นข้อยกเว้นการตรวจสอบค่าศูนย์

นั่นเป็นเหตุผลที่การอ้างอิงทั้งตัวชี้โมฆะและการอ้างอิงโมฆะสร้างข้อยกเว้นเดียวกัน

แล้วประเด็นที่สองล่ะ? การอ้างอิงตัวชี้ที่ไม่ถูกต้องใด ๆที่อยู่ในหน้าต่ำสุดของหน่วยความจำเสมือนทำให้เกิดข้อผิดพลาดของระบบปฏิบัติการเดียวกันและดังนั้นจึงเป็นข้อยกเว้นเดียวกัน

ทำไมสิ่งนี้ถึงสมเหตุสมผล? สมมุติว่าเรามีโครงสร้างที่มีสอง ints และตัวชี้ที่ไม่มีการจัดการเท่ากับ null หากเราพยายามยกเลิกการอ้างอิง int ตัวที่สองในโครงสร้างCLRจะไม่พยายามเข้าถึงที่เก็บข้อมูลที่ตำแหน่งศูนย์ มันจะเข้าถึงที่เก็บข้อมูลในตำแหน่งที่สี่ แต่ในทางตรรกะนี่เป็นข้อผิดพลาดแบบ null เพราะเราไปถึงที่อยู่นั้นผ่านทาง null

หากคุณกำลังทำงานกับรหัสที่ไม่ปลอดภัยและคุณได้รับข้อยกเว้น dereference เป็นโมฆะโปรดทราบว่าตัวชี้ที่ละเมิดไม่จำเป็นต้องเป็นโมฆะ มันสามารถเป็นตำแหน่งใด ๆ ในหน้าต่ำสุดและข้อยกเว้นนี้จะถูกสร้างขึ้น


55
บางทีนี่อาจเป็นความคิดเห็นที่โง่ แต่ไม่ได้เป็นวิธีแรกและดีที่สุดในการหลีกเลี่ยงปัญหานี้เพื่อเริ่มต้นวัตถุ สำหรับฉันหากข้อผิดพลาดนี้เกิดขึ้นมันมักจะเป็นเพราะฉันลืมที่จะเริ่มต้นบางอย่างเช่นองค์ประกอบอาร์เรย์ ฉันคิดว่ามันธรรมดาน้อยกว่าที่จะกำหนดวัตถุเป็นโมฆะแล้วอ้างอิงมัน อาจให้วิธีการแก้ปัญหาแต่ละข้อที่อยู่ติดกับคำอธิบาย ยังคงโพสต์ที่ดี
JPK

30
จะเกิดอะไรขึ้นถ้าไม่มีวัตถุใด ๆ แต่เป็นค่าที่ส่งคืนจากเมธอดหรือคุณสมบัติ?
John Saunders

6
ตัวอย่างหนังสือ / ผู้แต่งแปลกไปหน่อย .... Intellisense ทำงานอย่างไร? นี่คือสิ่งที่ฉันไม่ดีกับคอมพิวเตอร์ ...

5
@Will: การแก้ไขครั้งล่าสุดของฉันมีความช่วยเหลือหรือไม่ หากไม่ใช่โปรดระบุให้ชัดเจนยิ่งขึ้นเกี่ยวกับสิ่งที่คุณเห็นว่าเป็นปัญหา
จอห์นแซนเดอร์

6
@ JohnSaunders โอ้ไม่ฉันหมายถึงรุ่นตัวเริ่มต้นของวัตถุ new Book { Author = { Age = 45 } };การเริ่มต้นด้านในยังไง ... ฉันไม่สามารถนึกถึงสถานการณ์ที่ init ภายในจะทำงานได้ แต่มันก็รวบรวมและ intellisense ได้ ... เว้นแต่ว่าจะมี struct หรือไม่

311

ข้อยกเว้น NullReference - Visual Basic

NullReference ExceptionสำหรับVisual Basicไม่แตกต่างจากคนที่อยู่ในC # ท้ายที่สุดพวกเขาทั้งสองรายงานข้อยกเว้นเดียวกันที่กำหนดไว้ใน. NET Framework ซึ่งทั้งคู่ใช้ สาเหตุที่ไม่ซ้ำกับ Visual Basic นั้นเป็นของหายาก

คำตอบนี้จะใช้คำศัพท์ไวยากรณ์และบริบทของ Visual Basic ตัวอย่างที่ใช้มาจากคำถาม Stack Overflow ที่ผ่านมาจำนวนมาก นี่คือการเพิ่มความเกี่ยวข้องโดยใช้ประเภทของสถานการณ์ที่มักจะเห็นในโพสต์ มีคำอธิบายเพิ่มเติมอีกเล็กน้อยสำหรับผู้ที่อาจต้องการ ตัวอย่างที่คล้ายกับของคุณจะมากจดทะเบียนมีแนวโน้มที่นี่

บันทึก:

  1. นี่คือแนวคิดตาม: ไม่มีรหัสสำหรับคุณที่จะวางลงในโครงการของคุณ มีวัตถุประสงค์เพื่อช่วยให้คุณเข้าใจสิ่งที่ทำให้NullReferenceException(NRE) วิธีการค้นหาวิธีการแก้ไขและวิธีหลีกเลี่ยง NRE อาจเกิดขึ้นได้หลายวิธีดังนั้นจึงไม่น่าเป็นไปได้ที่คุณจะพบ แต่เพียงผู้เดียว
  2. ตัวอย่าง (จากโพสต์สแต็คโอเวอร์โฟลว์) ไม่แสดงวิธีที่ดีที่สุดในการทำบางสิ่งในตอนแรก
  3. โดยทั่วไปจะใช้วิธีการรักษาที่ง่ายที่สุด

ความหมายพื้นฐาน

ข้อความ"ไม่ได้ตั้งค่าวัตถุเป็นอินสแตนซ์ของวัตถุ"หมายความว่าคุณกำลังพยายามใช้วัตถุที่ยังไม่ได้เริ่มต้น สิ่งนี้ทำให้เดือดลงไปหนึ่งในสิ่งเหล่านี้:

  • รหัสของคุณประกาศตัวแปรวัตถุ แต่ไม่ได้เริ่มต้น (สร้างอินสแตนซ์หรือ ' สร้างอินสแตนซ์ ')
  • สิ่งที่รหัสของคุณสันนิษฐานว่าจะเริ่มต้นวัตถุไม่ได้
  • อาจเป็นไปได้ว่ารหัสอื่น ๆ ที่ทำให้วัตถุใช้ไม่ได้ก่อนกำหนด

การหาสาเหตุ

เนื่องจากปัญหาคือการอ้างอิงวัตถุซึ่งก็คือNothingคำตอบคือการตรวจสอบพวกเขาเพื่อหาที่หนึ่ง จากนั้นกำหนดว่าทำไมจึงไม่เริ่มต้น ถือเมาส์มากกว่าตัวแปรต่างๆและ Visual Studio (VS) จะแสดงค่าของพวกเขา - Nothingผู้กระทำผิดจะเป็น

จอแสดงผล debug IDE

คุณควรลบบล็อค 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 จะเตือนคุณว่าสิ่งนี้อาจเกิดขึ้น:

img

เมื่อประกาศว่าเป็นตัวแปรระดับโมดูล / ระดับตามที่ปรากฏเป็นกรณีด้วย 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องค์ประกอบอาจไม่มีอยู่


การควบคุม UI

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


แบบฟอร์ม Visual Basic

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 จะส่งผลเมื่อคุณพยายามอ้างอิง

สิ่งเหล่านี้ควรหาได้ง่ายในขณะนี้ที่คุณรู้ว่าคุณกำลังมองหา: VS แสดงข้อผิดพลาดในแบบของคุณ

"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) ใช้การรักษาที่เหมาะสม บล็อกแบบลอง / จับไม่ได้มีวัตถุประสงค์เพื่อซ่อนข้อยกเว้นจากบุคคลที่มีคุณสมบัติเหมาะสมในการแก้ไขปัญหา - นักพัฒนา


DBNull ไม่เหมือนไม่มีอะไรเลย

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

DataGridView

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

ตัวอย่างที่ 2 - ระวัง NewRow

xlWorkSheet = xlWorkBook.Sheets("sheet1")

For i = 0 To myDGV.RowCount - 1
    For j = 0 To myDGV.ColumnCount - 1
        For k As Integer = 1 To myDGV.Columns.Count
            xlWorkSheet.Cells(1, k) = myDGV.Columns(k - 1).HeaderText
            xlWorkSheet.Cells(i + 2, j + 1) = myDGV(j, i).Value.ToString()
        Next
    Next
Next

เมื่อคุณ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)

ในบางสถานการณ์การลองใช้ไอเท็ม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(เสมอ)


ข้อยกเว้นของ NullReference MSDN


226

สถานการณ์ก็คือเมื่อคุณโยนวัตถุโมฆะเป็นประเภทค่า ตัวอย่างเช่นรหัสด้านล่าง:

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 สร้างรหัสที่รวบรวมได้ดี ...


7
เยี่ยมมาก เส้นทางเดียวที่ควรหลีกเลี่ยง:DateTime x = (DateTime) o as DateTime? ?? defaultValue;
Serge Shultz

159

หมายความว่าตัวแปรในคำถามนั้นไม่ได้ชี้ไปที่อะไรเลย ฉันสามารถสร้างเช่นนี้โดย:

SqlConnection connection = null;
connection.Open();

นั่นจะทำให้เกิดข้อผิดพลาดเพราะในขณะที่ฉันประกาศตัวแปร " connection" มันไม่ได้ชี้ไปที่อะไรเลย เมื่อฉันพยายามโทรหาสมาชิก " Open" ไม่มีการอ้างอิงเพื่อแก้ไขและจะทำให้เกิดข้อผิดพลาด

เพื่อหลีกเลี่ยงข้อผิดพลาดนี้:

  1. เริ่มต้นวัตถุของคุณเสมอก่อนที่จะพยายามทำอะไรกับพวกเขา
  2. object == nullหากคุณไม่แน่ใจว่าวัตถุเป็นโมฆะตรวจสอบด้วย

เครื่องมือ Resharper ของ JetBrains จะระบุทุกที่ในรหัสของคุณที่มีความเป็นไปได้ของข้อผิดพลาดในการอ้างอิงแบบ null ซึ่งช่วยให้คุณสามารถตรวจสอบ null ได้ ข้อผิดพลาดนี้เป็นแหล่งที่มาของข้อบกพร่องอันดับหนึ่ง IMHO


3
เครื่องมือ Resharper ของ JetBrains จะระบุทุกที่ในรหัสของคุณที่มีความเป็นไปได้ของข้อผิดพลาดในการอ้างอิงแบบ null สิ่งนี้ไม่ถูกต้อง ฉันมีวิธีแก้ปัญหาที่ไม่มีการตรวจจับนั้น แต่โค้ดบางครั้งก็ส่งผลให้เกิดข้อยกเว้น ฉันสงสัยว่าบางครั้งมันตรวจไม่พบ - โดยพวกเขาอย่างน้อย - เมื่อมีการใช้มัลติเธรดเข้ามาเกี่ยวข้อง แต่ฉันไม่สามารถแสดงความคิดเห็นเพิ่มเติมได้เพราะฉันยังไม่ได้ระบุที่ตั้งของบั๊กของฉัน
j riv

แต่วิธีการแก้ไขเมื่อ NullReferenceException มาใน usign HttpContext.Current.Responce.Clear () มันไม่ได้รับการแก้ไขโดยวิธีการแก้ปัญหาข้างต้น เพราะในขณะที่สร้างวัตถุวัตถุของ HttpContext แล้วมีข้อผิดพลาดเกิดขึ้น "การแก้ปัญหาโอเวอร์โหลดล้มเหลวเนื่องจากไม่สามารถเข้าถึง 'ใหม่' ได้รับการยอมรับจำนวนข้อโต้แย้งนี้
Sunny Sandeep

157

มันหมายความว่ารหัสของคุณใช้ตัวแปรอ้างอิงวัตถุที่ตั้งค่าเป็นโมฆะ (เช่นมันไม่ได้อ้างอิงอินสแตนซ์ของวัตถุจริง)

เพื่อป้องกันข้อผิดพลาดวัตถุที่อาจเป็นโมฆะควรทดสอบเป็นโมฆะก่อนที่จะถูกนำมาใช้

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
}

96

โปรดทราบว่าไม่ว่าสถานการณ์จะเป็นสาเหตุหรือไม่ก็ตามใน. NET:

คุณพยายามที่จะใช้ตัวแปรอ้างอิงที่มีค่า/Nothing nullเมื่อค่าเป็นNothing/ nullสำหรับตัวแปรอ้างอิงนั่นหมายความว่ามันไม่ได้ถือการอ้างอิงกับอินสแตนซ์ของวัตถุใด ๆ ที่มีอยู่ในฮีป

คุณไม่เคยกำหนดบางสิ่งให้กับตัวแปรไม่เคยสร้างอินสแตนซ์ของค่าที่กำหนดให้กับตัวแปรหรือคุณตั้งค่าตัวแปรเท่ากับNothing/ nullด้วยตนเองหรือคุณเรียกใช้ฟังก์ชันที่ตั้งค่าตัวแปรให้Nothing/ nullสำหรับคุณ


87

ตัวอย่างของข้อยกเว้นนี้ที่ถูกโยนคือ: เมื่อคุณพยายามตรวจสอบบางสิ่งนั่นเป็นโมฆะ

ตัวอย่างเช่น:

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 พารามิเตอร์


87

อัปเดต 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') ในขณะที่ชนิดของค่ามีค่าอยู่เสมอ

ประเภทการอ้างอิง (ประเภทเหล่านี้ต้องถูกตรวจสอบ):

  • พลวัต
  • วัตถุ
  • เชือก

ประเภทค่า (คุณสามารถละเว้นสิ่งเหล่านี้ได้):

  • ประเภทตัวเลข
  • ประเภทหนึ่ง
  • ประเภทจุดลอยตัว
  • ทศนิยม
  • บูล
  • structs ที่ผู้ใช้กำหนด

6
-1: เนื่องจากคำถามคือ "NullReferenceException" คืออะไรประเภทค่าจึงไม่เกี่ยวข้อง
John Saunders

21
@ John Saunders: ฉันไม่เห็นด้วย ในฐานะนักพัฒนาซอฟต์แวร์เป็นสิ่งสำคัญอย่างยิ่งที่จะต้องแยกแยะความแตกต่างระหว่างค่าและประเภทการอ้างอิง คนอื่นจะสิ้นสุดการตรวจสอบว่าจำนวนเต็มเป็นโมฆะ
Fabian Bigler

5
จริงไม่ใช่ในบริบทของคำถามนี้
John Saunders

4
ขอบคุณสำหรับคำใบ้ ฉันปรับปรุงมันเล็กน้อยและเพิ่มตัวอย่างที่ด้านบน ฉันยังคิดว่าการกล่าวถึงการอ้างอิงและประเภทค่ามีประโยชน์
Fabian Bigler

5
ฉันคิดว่าคุณยังไม่ได้เพิ่มสิ่งใด ๆ ที่ไม่ได้อยู่ในคำตอบอื่น ๆ เนื่องจากคำถามจะให้ประเภทการอ้างอิงล่วงหน้า
John Saunders

78

อีกกรณีที่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) {
   // ...
}

2
สิ่งนี้สามารถเกิดขึ้นได้มากเมื่อยกเลิกการทำตัวแปร ฉันพบว่าเกิดขึ้นบ่อยครั้งในตัวจัดการเหตุการณ์หลังจากเปลี่ยนประเภทองค์ประกอบ UI แต่ลืมอัปเดตโค้ด
เบรนแดน

65

คุณกำลังใช้วัตถุที่มีการอ้างอิงค่า Null ดังนั้นมันจึงให้ข้อยกเว้นเป็นโมฆะ ในตัวอย่างค่าสตริงเป็นโมฆะและเมื่อตรวจสอบความยาวของมันจะเกิดข้อยกเว้นขึ้น

ตัวอย่าง:

string value = null;
if (value.Length == 0) // <-- Causes exception
{
    Console.WriteLine(value); // <-- Never reached
}

ข้อผิดพลาดข้อยกเว้นคือ:

ข้อยกเว้นที่ไม่ได้จัดการ:

System.NullReferenceException: การอ้างอิงวัตถุไม่ได้ตั้งค่าเป็นอินสแตนซ์ของวัตถุ ที่ Program.Main ()


1
ช่างลึกซึ้งเหลือเกิน! ฉันไม่เคยถือว่าค่าอ้างอิงเป็นโมฆะ ดังนั้นนี่คือวิธีที่ C # abstracts "NullPointer" huh? B / c ตามที่ฉันจำได้ใน C ++, NPE อาจเกิดจากการยกเลิกการกำหนดตัวชี้เริ่มต้น (เช่นประเภท ref ใน c #) ซึ่งค่าเริ่มต้นเกิดขึ้นเป็นที่อยู่ที่ไม่ได้จัดสรรให้กับกระบวนการนั้น (หลายกรณีนี้จะเป็น 0 โดยเฉพาะอย่างยิ่งใน C ++ รุ่นต่อมาที่ทำการเริ่มต้นอัตโนมัติซึ่งเป็นของ OS - f ด้วยมันและตาย beeotch (หรือเพียงแค่จับ sigkill OS โจมตีกระบวนการของคุณด้วย)
samis

64

ในขณะที่สิ่งที่ทำให้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) ที่ถูกส่งออกนอกรหัสของฉันและไม่ผ่านรหัสของฉัน


59

Simon Mourier ยกตัวอย่างนี้ :

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<>อินสแตนซ์มวย


42

การเพิ่มเคสเมื่อชื่อคลาสสำหรับเอนทิตีที่ใช้ในเฟรมเวิร์กเอนทิตีเหมือนกับชื่อคลาสสำหรับไฟล์โค้ด 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 ()


41

กรณีทั่วไปอีกกรณีหนึ่งที่อาจได้รับข้อยกเว้นนี้เกี่ยวข้องกับคลาสที่เยาะเย้ยในระหว่างการทดสอบหน่วย ไม่ว่าคุณจะใช้กรอบการเยาะเย้ยก็ตามคุณต้องทำให้แน่ใจว่าระดับที่เหมาะสมทั้งหมดของลำดับชั้นของคลาสนั้นถูกเยาะเย้ยอย่างเหมาะสม โดยเฉพาะอย่างยิ่งคุณสมบัติทั้งหมดHttpContextที่มีการอ้างอิงโดยรหัสภายใต้การทดสอบจะต้องล้อเลียน

ดูที่ " NullReferenceException เกิดขึ้นเมื่อทดสอบ AuthorizationAttribute แบบกำหนดเอง " สำหรับตัวอย่างที่ค่อนข้างละเอียด


40

ฉันมีมุมมองที่แตกต่างในการตอบคำถามนี้ คำตอบประเภทนี้"ฉันจะทำอะไรได้อีกบ้างเพื่อหลีกเลี่ยงปัญหานี้ "

เมื่อทำงานกับเลเยอร์ที่แตกต่างกันตัวอย่างเช่นในแอปพลิเคชัน 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();
    }
}

6
-1: นี่จัดการสถานการณ์เดียวเท่านั้น - นั่นคือการอ้างอิงที่ไม่ได้กำหนดค่าเริ่มต้น นี่เป็นสถานการณ์ส่วนน้อยสำหรับ NullReferenceException กรณีส่วนใหญ่เป็นความเข้าใจผิดอย่างง่าย ๆ ว่าวัตถุทำงานอย่างไร ถัดไปที่พบบ่อยที่สุดคือสถานการณ์อื่น ๆ ที่ผู้พัฒนาคิดว่าวัตถุนั้นจะเริ่มต้นโดยอัตโนมัติ
John Saunders

โดยทั่วไปการใช้การอ้างอิงไม่ได้ใช้เพื่อหลีกเลี่ยง NullReferenceException ฉันไม่เชื่อว่าคุณได้พบสถานการณ์ทั่วไปที่นี่ ไม่ว่าในกรณีใดถ้าคุณแก้ไขคำตอบของคุณให้มากขึ้นในรูปแบบของstackoverflow.com/a/15232518/76337ฉันจะลบ downvote
John Saunders

38

ในเรื่องของ"สิ่งที่ฉันควรทำเกี่ยวกับเรื่องนี้"อาจมีคำตอบมากมาย

วิธีที่เป็นทางการ "มากขึ้น" ของการป้องกันเงื่อนไขข้อผิดพลาดในขณะที่การพัฒนากำลังใช้การออกแบบโดยสัญญาในรหัสของคุณ ซึ่งหมายความว่าคุณต้องตั้งค่าคลาส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:มันเป็นมูลค่าการกล่าวขวัญว่าเป็นคำประกาศเกียรติคุณจากเบอร์แทรนด์เมเยอร์ในการเชื่อมต่อกับการออกแบบของเขาในการเขียนโปรแกรมภาษาไอเฟล


2
ฉันคิดว่าจะเพิ่มสิ่งนี้ตามที่ไม่มีใครพูดถึงและเท่าที่มันมีอยู่ในแนวทางฉันตั้งใจจะเพิ่มหัวข้อ
Nick Louloudakis

2
ขอบคุณสำหรับการเพิ่มคุณค่าให้กับหัวข้อ ฉันให้ความเห็นเกี่ยวกับการเพิ่มของคุณ ตอนนี้คนอื่นสามารถทำเช่นเดียวกัน
John Saunders

2
ฉันคิดว่านี่เป็นส่วนเพิ่มเติมที่คุ้มค่าสำหรับหัวข้อที่ระบุว่านี่เป็นกระทู้ที่มีผู้เข้าชมมาก ฉันเคยได้ยินสัญญาโค้ดมาก่อนและนี่เป็นคำเตือนที่ดีในการพิจารณาใช้
VoteCoffee

36

A NullReferenceExceptionถูกส่งออกมาเมื่อเราพยายามเข้าถึงคุณสมบัติของวัตถุ null หรือเมื่อค่าสตริงว่างเปล่าและเราพยายามเข้าถึงวิธีการสตริง

ตัวอย่างเช่น:

  1. เมื่อเมธอดสตริงของสตริงว่างเข้าถึง:

    string str = string.Empty;
    str.ToLower(); // throw null reference exception
  2. เมื่อคุณสมบัติของวัตถุ null เข้าถึง:

    Public Class Person {
        public string Name { get; set; }
    }
    Person objPerson;
    objPerson.Name  /// throw Null refernce Exception 

2
สิ่งนี้ไม่ถูกต้อง String.Empty.ToLower()จะไม่ส่งข้อยกเว้นการอ้างอิงเป็นโมฆะ เพราะมันหมายถึงสตริงที่แท้จริงแม้ว่าจะเป็นสตริงที่ว่างเปล่า (เช่น"") เนื่องจากสิ่งนี้มีวัตถุที่จะเรียกToLower()ใช้จึงไม่เหมาะสมที่จะแสดงข้อยกเว้นการอ้างอิงเป็นศูนย์
Kjartan

31

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
John Saunders

นอกจากนี้โปรดแสดงว่าบรรทัดใดที่ทำให้เกิดข้อยกเว้นและเพราะอะไร
John Saunders

ข้อผิดพลาดที่เกิดขึ้นใน MyOtherView.cshtml ซึ่งฉันไม่ได้รวมไว้ที่นี่เพราะรูปแบบไม่ได้ถูกส่งอย่างถูกต้อง (มันNull) ดังนั้นฉันรู้ว่าข้อผิดพลาดคือวิธีที่ฉันส่งแบบจำลองมา
Travis Heeter

22

คุณทำอะไรได้บ้าง

มีคำตอบที่ดีมากมายที่นี่อธิบายสิ่งที่อ้างอิงโมฆะและวิธีการแก้ปัญหามัน แต่มีน้อยมากเกี่ยวกับวิธีการป้องกันปัญหาหรืออย่างน้อยก็ทำให้ง่ายต่อการจับ

ตรวจสอบข้อโต้แย้ง

ตัวอย่างเช่นวิธีการสามารถตรวจสอบข้อโต้แย้งที่แตกต่างกันเพื่อดูว่าพวกเขาเป็นโมฆะและโยน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 แนะนำ "โอเปอเรเตอร์ที่มีเงื่อนไขเป็นโมฆะ" ซึ่งช่วยได้เล็กน้อย ด้วยคุณสมบัตินี้คุณสามารถอ้างอิงวัตถุซ้อนกันและถ้าหนึ่งของพวกเขาใด ๆ ที่เป็นผลตอบแทนที่แสดงออกทั้ง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)(!):


2
อย่าโยน NullReferenceException
John Saunders

@JohnSaunders กล้าถามทำไม (อย่างจริงจังว่าทำไม?)
ลูอิสเปเรซ

2
NullReferenceException มีวัตถุประสงค์เพื่อโยนโดย CLR หมายความว่ามีการอ้างอิงถึงโมฆะเกิดขึ้น ไม่ได้หมายความว่าการอ้างอิงถึงโมฆะจะเกิดขึ้นยกเว้นว่าคุณทำการตรวจสอบอย่างชาญฉลาดก่อน
John Saunders

ฉันเห็นประเด็นของคุณเกี่ยวกับความสับสน ฉันได้อัปเดตเป็นข้อยกเว้นปกติสำหรับตัวอย่างนี้และข้อยกเว้นที่กำหนดเองใน GitHub
Luis Perez

คำตอบที่ดีสำหรับคำถามพื้นฐานเช่นนั้น มันไม่ได้เลวร้ายเมื่อมันเป็นรหัสของคุณที่ล้มเหลว มันน่ากลัวเมื่อมาจากส่วนลึกของห้องสมุดบุคคลที่สามที่คุณใช้ในเชิงพาณิชย์และฝ่ายบริการลูกค้ายืนยันว่าจะต้องเป็นรหัสของคุณที่ทำให้เกิดปัญหา และคุณไม่แน่ใจว่ามันไม่สมบูรณ์และโครงการทั้งหมดต้องหยุดชะงักฉันจริง ๆ แล้วคิดว่านี่อาจเป็นตัวอย่างที่เหมาะสมสำหรับหลุมฝังศพของฉัน: "การอ้างอิงวัตถุไม่ได้ถูกตั้งค่าเป็นตัวอย่างของวัตถุ"
Darrel Lee

10

น่าสนใจไม่มีคำตอบในหน้านี้ที่กล่าวถึงกรณีขอบทั้งสองหวังว่าไม่มีใครคิดถ้าฉันเพิ่มพวกเขา:

กรณี Edge # 1: การเข้าถึงพจนานุกรมพร้อมกัน

พจนานุกรมทั่วไปใน. NET ไม่ได้เป็นเธรดที่ปลอดภัยและบางครั้งพวกเขาอาจโยน a NullReferenceหรือ (บ่อยครั้งมากขึ้น) KeyNotFoundExceptionเมื่อคุณพยายามเข้าถึงคีย์จากสองเธรดที่เกิดขึ้นพร้อมกัน ข้อยกเว้นค่อนข้างทำให้เข้าใจผิดในกรณีนี้

Edge case # 2: รหัสที่ไม่ปลอดภัย

ถ้า a NullReferenceExceptionถูกขว้างด้วยunsafeรหัสคุณอาจดูตัวแปรพอยน์เตอร์ของคุณและตรวจสอบIntPtr.Zeroสิ่งนั้น ซึ่งเป็นสิ่งเดียวกัน ("ข้อยกเว้นตัวชี้โมฆะ") แต่ในรหัสที่ไม่ปลอดภัยตัวแปรมักถูกส่งไปยังประเภท / อาร์เรย์มูลค่า ฯลฯ และคุณกระแทกหัวของคุณกับผนัง ข้อยกเว้น

(อีกสาเหตุสำหรับการไม่ใช้รหัสที่ไม่ปลอดภัยเว้นแต่คุณต้องการโดยวิธี)


5
ตัวอย่างพจนานุกรมของคุณไม่ใช่ตัวพิมพ์ขอบ ถ้าวัตถุนั้นไม่ปลอดภัยต่อเธรดให้ใช้จากหลายเธรดสร้างผลลัพธ์แบบสุ่ม ตัวอย่างรหัสที่ไม่ปลอดภัยของคุณแตกต่างจากnullที่ใด?
John Saunders

10

คุณสามารถแก้ไข 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

สำหรับรายละเอียดเพิ่มเติม: ผู้ประกอบการที่มีเงื่อนไข


9

บรรทัดข้อผิดพลาด "การอ้างอิงวัตถุไม่ได้ถูกตั้งค่าเป็นอินสแตนซ์ของวัตถุ" ระบุว่าคุณไม่ได้กำหนดวัตถุอินสแตนซ์ให้กับการอ้างอิงวัตถุและคุณยังคงเข้าถึงคุณสมบัติ / วิธีการของวัตถุนั้น

ตัวอย่างเช่นสมมติว่าคุณมีคลาสชื่อ 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;  
     }
}

4

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


3

ในแง่ง่าย:

คุณกำลังพยายามเข้าถึงวัตถุที่ไม่ได้สร้างขึ้นหรือไม่อยู่ในหน่วยความจำในปัจจุบัน

ดังนั้นวิธีแก้ไขปัญหานี้:

  1. ดีบั๊กและปล่อยให้ตัวดีบักหยุดทำงาน ... มันจะนำคุณไปยังตัวแปรที่ถูกทำลาย ... ตอนนี้งานของคุณคือการแก้ไขสิ่งนี้ .. การใช้คำค้นหาใหม่ในที่ที่เหมาะสม

  2. ถ้ามันเกิดจากคำสั่งฐานข้อมูลบางอย่างเพราะวัตถุไม่ได้อยู่แล้วสิ่งที่คุณต้องทำคือการตรวจสอบเป็นโมฆะและจัดการกับมัน:

    if (i == null) {
        // Handle this
    }
  3. สิ่งที่ยากที่สุด .. หากGCเก็บรวบรวมวัตถุอยู่แล้ว ... โดยทั่วไปจะเกิดขึ้นหากคุณพยายามค้นหาวัตถุโดยใช้สตริง ... นั่นคือการค้นหาโดยใช้ชื่อของวัตถุก็อาจเกิดขึ้นได้ว่า GC อาจมีอยู่แล้ว ล้างมัน ... นี่หายากและจะกลายเป็นปัญหา ... วิธีที่ดีกว่าในการจัดการกับปัญหานี้คือทำการตรวจสอบ null ในทุกที่ที่จำเป็นในระหว่างกระบวนการพัฒนา สิ่งนี้จะช่วยคุณประหยัดเวลาได้มาก

โดยการค้นหาตามชื่อฉันหมายถึงกรอบบางอย่างช่วยให้คุณสามารถ FIndObjects โดยใช้สตริงและรหัสอาจมีลักษณะเช่นนี้: FindObject ("ObjectName");


3
หากคุณมีการอ้างอิงไปยังวัตถุดังนั้น GC จะไม่ทำความสะอาด
John Saunders

2
ถ้าคุณใช้สิ่งต่าง ๆ เช่น FindObject ("ชื่อของวัตถุ") ไม่มีทางที่ GC จะรู้ก่อนที่คุณจะอ้างอิงวัตถุนั้น .. นี่คือสิ่งที่พยายามอธิบาย .. สิ่งเหล่านี้เกิดขึ้นที่รันไทม์
Akash Gutha

2
มีเฟรมเวิร์กบางตัวที่ให้ฟังก์ชันการทำงานนี้ใน C # เช่น Unity คำถามไม่มีอะไรเกี่ยวข้องกับ BCl ค้นหาอินเทอร์เน็ตก่อนที่จะวิจารณ์มีฟังก์ชั่นมากมายเช่นเดียวกับพวกเขาและสำหรับข้อมูลประเภทที่ฉันใช้งานได้ทุกวัน ตอนนี้โปรดบอกฉันว่าคำตอบไม่ได้ทำให้รู้สึกกังวล
Akash Gutha

2
docs.unity3d.com/ScriptReference/ … ตรวจสอบลิงค์และแก้ไข urself mr.expert: p
Akash Gutha

ตัวอย่างที่ฉันเห็นในลิงก์ของคุณกำหนดผลลัพธ์ของ GameObject.Find ให้กับเขตข้อมูลสมาชิก เป็นข้อมูลอ้างอิงและ GC จะไม่รวบรวมจนกว่าจะมีการรวบรวมวัตถุที่มี
John Saunders

1

แท้จริงวิธีที่ง่ายที่สุดในการแก้ไข NullReferenceExeption มีสองวิธี หากคุณมี GameObject เช่นแนบสคริปต์และตัวแปรชื่อ rb (rigidbody) ตัวแปรนี้จะเริ่มเป็นโมฆะเมื่อคุณเริ่มเกม
นี่คือเหตุผลที่คุณได้รับ NullReferenceExeption เนื่องจากคอมพิวเตอร์ไม่มีข้อมูลที่เก็บอยู่ในตัวแปรนั้น

ฉันจะใช้ตัวแปร RigidBody เป็นตัวอย่าง
เราสามารถเพิ่มข้อมูลได้อย่างง่ายดายจริงๆในสองสามวิธี:

  1. เพิ่ม RigidBody ให้กับวัตถุของคุณด้วย AddComponent> Physics> Rigidbody
    จากนั้นไปที่สคริปต์และประเภทของคุณrb = GetComponent<Rigidbody>();
    บรรทัดของรหัสนี้ทำงานได้ดีที่สุดภายใต้Start()หรือAwake()หน้าที่ของคุณ
  2. คุณสามารถเพิ่มองค์ประกอบโดยทางโปรแกรมและกำหนดตัวแปรในเวลาเดียวกันด้วยรหัสหนึ่งบรรทัด: rb = AddComponent<RigidBody>();

หมายเหตุเพิ่มเติม: หากคุณต้องการความสามัคคีในการเพิ่มองค์ประกอบให้กับวัตถุของคุณและคุณอาจลืมที่จะเพิ่มองค์ประกอบหนึ่งคุณสามารถพิมพ์ข้อความ[RequireComponent(typeof(RigidBody))]ประกาศของชั้นเรียนได้ (พื้นที่ด้านล่างการใช้ทั้งหมดของคุณ)
สนุกและมีเกมทำสนุก!


-1

หากเราพิจารณาสถานการณ์ทั่วไปที่สามารถโยนข้อยกเว้นนี้ได้การเข้าถึงคุณสมบัติด้วยวัตถุที่ด้านบน

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

-3

นี้เป็นพื้นเป็นข้อยกเว้นที่อ้างอิง 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 ได้ก็อาจเป็นสาเหตุของข้อยกเว้นนี้

จะหลีกเลี่ยงได้อย่างไร?

มีหลายวิธีและวิธีการหลีกเลี่ยงข้อยกเว้นที่มีชื่อเสียงนี้:

  1. การตรวจสอบอย่างชัดเจน: เราควรปฏิบัติตามประเพณีในการตรวจสอบวัตถุคุณสมบัติวิธีการอาร์เรย์และคอลเลกชันไม่ว่าจะเป็นโมฆะ สิ่งนี้สามารถนำไปใช้งานได้ง่ายโดยใช้ข้อความสั่งเงื่อนไขเช่น if-else if-else เป็นต้น

  2. การจัดการข้อยกเว้น: หนึ่งในวิธีสำคัญในการจัดการข้อยกเว้นนี้ การใช้บล็อก try-catch-catch ในที่สุดเราสามารถควบคุมข้อยกเว้นนี้และเก็บบันทึกของมันได้ สิ่งนี้มีประโยชน์มากเมื่อใบสมัครของคุณอยู่ในขั้นตอนการผลิต

  3. ตัวดำเนินการ Null: ตัวดำเนินการ Null Coalescing และตัวดำเนินการเงื่อนไข Null สามารถใช้ประโยชน์ได้ในขณะที่ตั้งค่าให้กับวัตถุตัวแปรคุณสมบัติและเขตข้อมูล

  4. ดีบักเกอร์: สำหรับนักพัฒนาเรามีอาวุธขนาดใหญ่สำหรับการดีบักกับเรา ถ้าหากเราเผชิญกับ NullReferenceException ระหว่างการพัฒนาเราสามารถใช้ดีบักเกอร์เพื่อไปยังแหล่งที่มาของข้อยกเว้น

  5. วิธีที่สร้างขึ้น: วิธีการของระบบเช่น GetValueOrDefault (), IsNullOrWhiteSpace () และ IsNullorEmpty () ตรวจสอบ nulls และกำหนดค่าเริ่มต้นหากมีค่า null

มีคำตอบที่ดีอยู่แล้วที่นี่แล้ว นอกจากนี้คุณยังสามารถตรวจสอบรายละเอียดเพิ่มเติมเกี่ยวกับตัวอย่างบนของบล็อก

หวังว่านี่จะช่วยได้เช่นกัน!


โดยทั่วไปคุณคัดลอกครึ่งหนึ่งของโพสต์บล็อกนั้นและไม่ได้เพิ่มอะไรใหม่ที่คำตอบปัจจุบันไม่ได้ตอบ
CodeCaster

@codecaster มันคือการคัดลอกเมื่อคุณเขียนสรุปจากบล็อกของคุณเอง ฉันรู้ว่าไม่มีอะไรใหม่ในคำตอบของฉันและไม่มีอะไรใหม่ที่คำตอบก่อนหน้าไม่มี แต่ฉันต้องการมีส่วนร่วมในวิธีที่ซับซ้อนมากขึ้นและให้ผู้อื่นเข้าใจวิธีที่ฉันเข้าใจ จะดีใจแม้ว่าจะช่วยคนคนเดียว โดยสุจริต
Wasim

-4

หากมีใครได้รับข้อความนี้ระหว่างการบันทึกหรือการคอมไพล์บิลด์ให้ปิดไฟล์ทั้งหมดแล้วเปิดไฟล์ใด ๆ เพื่อคอมไพล์และบันทึก

สำหรับฉันเหตุผลคือฉันได้เปลี่ยนชื่อไฟล์และไฟล์เก่าก็ยังคงเปิดอยู่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.