การอ้างอิงอ็อบเจ็กต์ไม่ได้ตั้งค่าเป็นอินสแตนซ์ของอ็อบเจ็กต์เหตุใด. NET จึงไม่แสดงว่าอ็อบเจ็กต์ใดเป็น "null"


103

เกี่ยวกับข้อความยกเว้น. NET ที่ไม่สามารถจัดการได้นี้:

การอ้างอิงวัตถุไม่ได้ตั้งค่าเป็นตัวอย่างของวัตถุ

เหตุใด. NET จึงไม่แสดงว่าวัตถุnullใด

ฉันทราบว่าสามารถตรวจสอบnullและแก้ไขข้อผิดพลาดได้ อย่างไรก็ตามเหตุใด. NET จึงไม่ช่วยชี้ให้เห็นว่าวัตถุใดมีการอ้างอิงที่เป็นโมฆะและนิพจน์ใดที่เรียกใช้NullReferenceException?

c#  .net 

2
เมื่อสิ่งนี้เกิดขึ้นให้เขียนบรรทัดที่เกิดขึ้นใหม่เพื่อให้ตรวจสอบผลลัพธ์ที่เป็นไปได้แต่ละรายการว่าเป็นค่าว่างก่อนจากนั้นคุณจะรู้ว่ามันคืออะไร ไม่ว่าจะเป็นอย่างนั้นหรือมีการติดดีบักเกอร์ที่น่าทึ่งของ Visual Studio ซึ่งจะหยุดการทำงานทันทีข้อยกเว้นเกิดขึ้นและให้คุณเห็นว่าอะไรเป็นโมฆะ :)
Patashu

5
ไม่จริงเขาแค่ถามว่าทำไม. NET framework ไม่ช่วยให้โปรแกรมเมอร์แสดงว่าวัตถุใดเป็นโมฆะ ฉันเดาว่ามันเป็นโทษด้านประสิทธิภาพ (คุณต้องการการไตร่ตรอง) แต่ฉันก็ไม่แน่ใจเหมือนกัน
bas

1
@bas: แม้ว่าจะเป็นเรื่องจริง แต่คำถามก็ทำให้เข้าใจผิดได้เล็กน้อยว่าควรถามเกี่ยวกับ "ส่วนหนึ่งของนิพจน์" ไม่ใช่ "วัตถุ" นอกจากนี้ยังอธิบายว่าเหตุใดเพียงการไตร่ตรองจึงไม่ช่วยได้ แต่จำเป็นต้องมีข้อมูลการดีบักอย่างละเอียด
หรือ Mapper

4
ฉันยังอยากรู้คำตอบ มันคล้ายกับข้อยกเว้น. net ที่ไม่ได้ช่วยชี้ให้เห็นว่าคีย์ใดไม่มีอยู่ในพจนานุกรม นอกจากนี้ฉันไม่เข้าใจการอุทิศให้กับคำถาม
bas

12
คำศัพท์โปรด: วัตถุไม่เป็นโมฆะ การอ้างอิงวัตถุอาจเป็นไปได้ แต่การอ้างอิงวัตถุเป็นเพียงตำแหน่งในหน่วยความจำ - จะช่วยคุณได้อย่างไรเว้นแต่คุณจะติดดีบักเกอร์อยู่แล้ว
Oskar Berggren

คำตอบ:


169

(สำหรับข้อมูลเกี่ยวกับตัวช่วยข้อยกเว้นใหม่ใน Visual Studio 2017 ดูส่วนท้ายของคำตอบนี้)


พิจารณารหัสนี้:

String s = null;
Console.WriteLine(s.Length);

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

เพื่อให้เข้าใจว่าเหตุใดคุณจึงไม่ได้รับข้อมูลนั้นคุณควรจำไว้ว่าไม่ใช่แหล่งข้อมูล C # ที่ดำเนินการ แต่เป็น IL:

IL_0001: ldnull      
IL_0002: stloc.0 // วินาที
IL_0003: ldloc.0 // วินาที
IL_0004: callvirt System.String.get_Length
IL_0009: เรียก System.Console.WriteLine

เป็นcallvirtopcode ที่พ่นNullReferenceExceptionและทำเช่นนั้นเมื่ออาร์กิวเมนต์แรกบนสแต็กการประเมินเป็นการอ้างอิงที่ว่าง (อันที่โหลดโดยใช้ldloc.0)

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


เพื่อหลีกเลี่ยงปัญหานี้ฉันขอแนะนำให้คุณทำการตรวจสอบอาร์กิวเมนต์ที่ว่างเปล่าในการเรียกวิธีสาธารณะทั้งหมด (เว้นแต่คุณจะอนุญาตการอ้างอิง null):

public void Foo(String s) {
  if (s == null)
    throw new ArgumentNullException("s");
  Console.WriteLine(s.Length);
}

ถ้า null ถูกส่งไปยังเมธอดคุณจะได้รับข้อยกเว้นที่อธิบายได้อย่างชัดเจนว่าปัญหาคืออะไร (นั่นsคือโมฆะ)


สี่ปีต่อมา Visual Studio 2017 มีตัวช่วยข้อยกเว้นใหม่ที่จะพยายามบอกว่าอะไรเป็นโมฆะเมื่อมีการNullReferenceExceptionโยน มันยังสามารถให้ข้อมูลที่จำเป็นแก่คุณได้เมื่อเป็นค่าส่งคืนของเมธอดที่เป็นโมฆะ:

ตัวช่วยข้อยกเว้น Visual Studio 2017

โปรดทราบว่าสิ่งนี้ใช้ได้เฉพาะในรุ่น DEBUG เท่านั้น


5
หมายเลขบรรทัดและชื่อไฟล์ต้นฉบับไม่ได้ถูกจัดเก็บไว้ในรหัส IL เองใช่หรือไม่ แต่สามารถทำให้พร้อมใช้งานสำหรับการดีบัก
หรือผู้ทำแผนที่

4
@MartinLiversage: แน่นอน ดังนั้นคำถามเดือดลงไป: nullทำไมไม่ได้มีข้อมูลเพียงพอที่เก็บไว้ในไฟล์สัญลักษณ์ที่ยังบอกสิ่งที่แสดงออกในรหัสที่ได้รับการประเมินเพื่อ
หรือ Mapper

2
@MartinLiversage: ไฟล์สัญลักษณ์สามารถเข้าถึงได้ในลักษณะที่ว่าเมื่อมีข้อยกเว้นพร้อมกับข้อความยกเว้นไฟล์ต้นทางและหมายเลขบรรทัดสามารถแสดงในเอาต์พุตของดีบักเกอร์ ดังนั้นคำถามคืออะไรคือเหตุผลที่ไม่รวมข้อมูลเพิ่มเติมเกี่ยวกับสิ่งที่ส่งคืนnull- โปรดทราบว่า OP ไม่ได้อ้างว่าเขาหรือเธอต้องการทราบว่าสำหรับรุ่นที่วางจำหน่ายเช่นกันการสร้างดีบักอาจเพียงพอ
หรือ Mapper

3
อืมและเราไม่ควรลืมว่าไม่ใช่แม้กระทั่ง IL ที่รันจริง แต่เป็นโค้ดเนทีฟที่สร้างจากรันไทม์
Oskar Berggren

2
@MartinLiversage: ไม่มีใครในคำถามนี้อ้างว่าเราต้องการสิ่งนี้ได้รับการสนับสนุนอย่างสมบูรณ์แบบสำหรับการสร้างรุ่น อย่างไรก็ตามฉันไม่ค่อยเห็นปัญหาในการเชื่อมโยง IL opcode ที่ใช้การอ้างอิงวัตถุ (ซึ่งอาจกลายเป็นnull) กับบรรทัดไฟล์ต้นฉบับและคอลัมน์ที่ส่งคืนการอ้างอิงวัตถุนั้น
หรือผู้ทำแผนที่

9

คุณต้องการให้ข้อความแสดงข้อผิดพลาดในกรณีต่อไปนี้มีลักษณะอย่างไร?

AnyObject.GetANullObject().ToString();

private object GetANullObject()
{
  return null;
}

ไม่มีชื่อตัวแปรที่จะรายงานที่นี่!


2
ฉันสงสัยว่า OP กำลังมองหานิพจน์ในซอร์สโค้ดที่ส่งคืนค่าว่างไม่ใช่วัตถุ ฉันได้เพิ่มความคิดเห็นที่เกี่ยวข้องในคำถามและหวังว่าเขาหรือเธอจะเคลียร์สิ่งต่างๆ หากข้อสงสัยของฉันถูกต้อง OP จะคาดหวังบางอย่างเช่นObject reference obtained from AnyObject.GetANullObject() not set to an instance of an object.เดียวกับข้อความแสดงข้อผิดพลาด
หรือ Mapper

1
@ORMapper ฉันเห็นด้วย ฉันจะใส่ "คำตอบ" ในความคิดเห็นใน OP ถ้าฉันมีคะแนนชื่อเสียงมากพอที่จะแสดงความคิดเห็น!
romar

1
"การอ้างอิงว่างที่พยายามเรียกเมธอด ToString () ของคลาส XYZ" จะมีประโยชน์มากกว่าที่เราได้รับในตอนนี้
Michael Levy

สิ่งที่มีประโยชน์ที่สุดคือการติดตามสแต็กที่แสดงในแต่ละระดับการเรียกว่าบรรทัดใดในไฟล์ใดส่งผลให้เกิดข้อผิดพลาด เดี๋ยวก่อน ... นั่นคือสิ่งที่ทำตอนนี้!
Jim Balter

1

นั่นคือไม่เกินวิศวกรของ Microsoft ที่จะตอบ แต่คุณสามารถใช้ดีบักเกอร์และเพิ่มนาฬิกาเพื่อดูว่าตัวใดมีปัญหา

อย่างไรก็ตามข้อยกเว้นคือNullReferenceExceptionซึ่งหมายถึงการอ้างอิงไม่ได้อยู่ คุณไม่สามารถรับวัตถุที่ยังไม่ได้สร้างขึ้นเลย

but why .NET don't tell us which object is null? เพราะมันไม่รู้ว่าวัตถุใดเป็นโมฆะ วัตถุนั้นไม่มีอยู่จริง!

เช่นเดียวกับกรณีที่ฉันพูดว่า C # ถูกคอมไพล์เป็นรหัส. NET IL รหัส. NET IL ไม่ทราบชื่อหรือนิพจน์ รู้เฉพาะการอ้างอิงและตำแหน่งของพวกเขา ที่นี่คุณไม่สามารถรับสิ่งที่ไม่มีอยู่จริงได้ ไม่มีนิพจน์หรือชื่อตัวแปร

ปรัชญา: คุณไม่สามารถทำออมเล็ตได้หากคุณไม่มีไข่ตั้งแต่แรก


3
นั่นไม่ใช่คำตอบเช่นกัน :)
bas

คุณจะได้รับข้อมูลอ้างอิงได้อย่างไรหากไม่มีอยู่ @Bas
Aniket Inge

4
"นั่นคือไม่เกินวิศวกรของ Microsoft ที่จะตอบ" ดังนั้นให้พวกเขาเข้าใจเรื่องนี้แทนที่จะระบุสิ่งที่ชัดเจน
bas

@ อย่างน้อยที่สุดเราสามารถตัดสินใจได้ว่าอะไรคือเหตุผล ในทางตรรกะวัตถุไม่มีอยู่ คุณจะจับมันได้อย่างไรโดยมีข้อยกเว้นแล้วพิมพ์ชื่อของวัตถุ? มันไม่มีอยู่จริง ไม่ได้อยู่ในกอง ..
Aniket Inge

ดังนั้นคำตอบคือแทบจะเป็นไปไม่ได้เลยที่จะชี้ให้เห็นว่าวัตถุใดมีค่า nullreference? นั่นเป็นคำตอบเช่นกัน ฉันไม่ได้ระบุว่าฉันรู้ฉันแค่ชอบคำถาม :) +1 สำหรับความพยายามทั้งหมด: p
bas

1

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


-2

คำถามที่ดี. กล่องข้อความสั้น ๆ ไร้ประโยชน์ แม้ว่าจะถูกฝังลึกไปหนึ่งไมล์จากข้อกำหนดการอ้างอิง แต่คลาสหรือแอสเซมบลีหรือไฟล์หรือข้อมูลอื่น ๆ จะดีกว่าสิ่งที่พวกเขาให้ในปัจจุบัน (อ่าน: ดีกว่าไม่มีอะไรเลย)

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

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