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


39

เรากำลังเปิดตัวระบบและบางครั้งเราได้รับข้อยกเว้นที่มีชื่อเสียงพร้อมกับข้อความNullReferenceExceptionObject reference not set to an instance of an object

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

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

ตอนนี้มีบางอย่างหมกมุ่นอยู่ในใจฉันมาหลายเดือนแล้วและนั่นก็คือ:

เหตุใด. NET จึงไม่ให้ชื่อเราหรืออย่างน้อยก็ประเภทของการอ้างอิงวัตถุซึ่งเป็นโมฆะ? . ไม่เข้าใจประเภทจากการสะท้อนกลับหรือแหล่งอื่น ๆ

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

อัปเดต: ข้อยกเว้นThe system cannot find the file specifiedมีลักษณะเดียวกัน คุณไม่พบไฟล์ใดจนกว่าคุณจะแนบกับกระบวนการและดีบัก ฉันเดาว่าข้อยกเว้นประเภทนี้จะฉลาดขึ้น มันจะดีกว่าไหมถ้า. NET สามารถบอกเราc:\temp.txt doesn't exist.แทนข้อความทั่วไปได้? ในฐานะนักพัฒนาฉันลงคะแนนใช่


14
ข้อยกเว้นควรมีการติดตามสแต็กที่มีหมายเลขบรรทัด ฉันจะเริ่มการสอบสวนจากที่นั่นดูทุก ๆ วัตถุที่เข้าถึงได้ในบรรทัดนั้น
PersonalNexus

2
นอกจากนี้ฉันยังสงสัยอยู่เสมอว่าทำไมกล่องโต้ตอบตัวช่วยยกเว้นใน Visual Studio จึงมีคำแนะนำ "มีประโยชน์" เพื่อใช้newในการสร้างอินสแตนซ์ของคลาส คำใบ้ดังกล่าวเมื่อใดที่ทุกคนช่วยจริงๆ
PersonalNexus

1
หากคุณมีสายวัตถุสิบสายคุณจะมีปัญหากับข้อต่อในการออกแบบของคุณ
Pete Kirkham

4
ดูคำถามนี้stackoverflow.com/questions/14787580/…

9
ฉันชอบวิธีที่คำตอบของคำถามนี้อยู่ในบรรทัดของ "ใช้ตัวดีบั๊กบันทึกข้อผิดพลาดตรวจสอบความผิดของคุณ แต่อย่างใด" ซึ่งไม่ตอบคำถาม แต่โทษคุณ เฉพาะใน stackoverflow เท่านั้นที่มีคนให้คำตอบแก่คุณ (ซึ่งฉันคิดว่าเป็นค่าใช้จ่ายที่มากเกินไปสำหรับ VM ที่จะติดตาม) แต่จริงๆแล้วคนเท่านั้นที่สามารถตอบคำถามนี้ได้อย่างถูกต้องคือใครบางคนจาก microsoft ที่ทำงานในกรอบ
Rocklan

คำตอบ:


28

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

  • มี NullReferenceException
  • คุณไม่ได้ป้องกันมันในแบบที่คุณรู้ว่าทำไม / เกิดขึ้นที่ไหน
  • และยังอาจ: วิธีการที่ต้องการวัตถุ 20 ชิ้นดูเหมือนจะปิดไปเล็กน้อย

ฉันเป็นแฟนตัวยงของการตรวจสอบทุกอย่างก่อนที่สิ่งต่าง ๆ จะเริ่มผิดและให้ข้อมูลที่ดีกับนักพัฒนา ในระยะสั้น: เขียนตรวจสอบการใช้ArgumentNullExceptionและชอบและเขียนชื่อตัวเอง นี่คือตัวอย่าง:

void Method(string a, SomeObject b)
{
    if (a == null) throw ArgumentNullException("a");
    if (b == null) throw ArgumentNullException("b");

    // See how nice this is, and what peace of mind this provides? As long as
    // nothing modifies a or b you can use them here and be 100% sure they're not
    // null. Should they be when entering the method, at least you know which one
    // is null.
    var c = FetchSomeObject();
    if(c == null)
    {
        throw InvalidOperationException("Fetching B failed!!");
    }

    // etc.
}

นอกจากนี้คุณยังสามารถดูในสัญญารหัสมันมีนิสัยใจคอ แต่มันทำงานได้ดีและช่วยให้คุณพิมพ์บางอย่าง


17
@ SaeedNeamati คุณไม่ได้หมายความว่าเพราะคุณมี codebase ขนาดใหญ่คุณไม่ควรทำการตรวจสอบข้อผิดพลาดที่เหมาะสมใช่ไหม? ยิ่งโครงการมีขนาดใหญ่ขึ้นเท่าไรสิ่งสำคัญก็คือบทบาทหรือการตรวจสอบข้อผิดพลาดและการรายงาน
stijn

6
+1 สำหรับคำแนะนำที่ดีแม้ว่าจะไม่ได้ตอบคำถามจริง (ซึ่งอาจมีเพียง Anders Hejlsberg เท่านั้นที่สามารถตอบได้)
Ross Patterson

12
+1 สำหรับคำแนะนำที่ดีเยี่ยม @ SaeedNeamati คุณควรฟังคำแนะนำนี้ ปัญหาของคุณเกิดจากความประมาทและการขาดความเป็นมืออาชีพในรหัส หากคุณไม่มีเวลาเขียนโค้ดที่ดีแสดงว่าคุณมีปัญหาที่ใหญ่กว่ามาก ...
MattDavey

3
หากคุณไม่ได้มีเวลาที่จะเขียนดีรหัสแล้วคุณแน่นอนไม่ได้มีเวลาที่จะเขียนไม่ดีรหัส การเขียนรหัสที่ไม่ดีใช้เวลานานกว่าการเขียนรหัสที่ดี ถ้าคุณจริงๆไม่สนใจเกี่ยวกับข้อบกพร่อง และถ้าคุณไม่สนใจข้อบกพร่องจริงๆเขียนโปรแกรมเลยทำไม?
MarkJ

5
@ SaeedNeamati I only say that checking every object to get sure that it's not null, is not a good methodอย่างจริงจัง มันเป็นวิธีที่ดีที่สุด และไม่ใช่เพียง null ตรวจสอบทุกอาร์กิวเมนต์เพื่อหาค่าที่สมเหตุสมผล ยิ่งคุณพบข้อผิดพลาดเร็วขึ้นเท่าใดก็ยิ่งหาสาเหตุได้ง่ายขึ้น คุณไม่ต้องการที่จะย้อนกลับหลายระดับในการติดตามสแต็กเพื่อค้นหาการโทรที่ก่อให้เกิด
jgauffin

19

ควรแสดงสิ่งที่คุณพยายามโทรออกจริงๆ มันเหมือนกับการพูดว่า "มีปัญหาคุณต้องแก้ไขฉันรู้ว่ามันคืออะไรฉันจะไม่บอกคุณคุณจะไปคิดออก" บิตเหมือนคำตอบครึ่งหนึ่งของ Stack Overflow นี้แดกดัน

ยกตัวอย่างเช่นมีประโยชน์อย่างไรถ้าคุณได้รับ ...

Object reference (HttpContext.Current) not set to instance of an object

... ? หากต้องการเข้าไปในโค้ดให้ก้าวผ่านมันออกไปและคิดว่าสิ่งที่คุณพยายามโทรหานั้นใช้ได้nullแต่ทำไมไม่เพียงแค่ให้ความช่วยเหลือกับเราสักหน่อยล่ะ?

ฉันยอมรับว่ามักจะมีประโยชน์ในการก้าวผ่านโค้ดเพื่อรับคำตอบ (เพราะคุณอาจจะหาข้อมูลเพิ่มเติม) แต่บ่อยครั้งมากที่ความประหยัดและความยุ่งยากจะได้รับการบันทึกหากNullReferenceExceptionข้อความมีลักษณะเหมือนตัวอย่างด้านบน

เพียงแค่พูด


รันไทม์เป็นอย่างไรควรรู้ว่าเป็นโมฆะอย่างไร
จะ

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

เห็นด้วยและผมว่าเหมือนกันสำหรับKeyNotFoundExceptionและ annoyances อื่น ๆ อีกมากมาย ...
sinelaw

ที่จริงแล้วในกรณีที่ไม่มีการลงทะเบียนแบบโมฆะสิ่งนี้ดูเหมือนจะเป็นข้อ จำกัด ในวิธีที่การอ้างสิทธิ์CLR (ขอบคุณ Shahrooz Jefri ความคิดเห็นของคำถาม OP)
sinelaw


4

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

ได้รับมันจะไม่ช่วยคุณในกรณีนี้:

Foo.Bar.Baz.DoSomething()

การบอกไม่ถามหลักการสามารถช่วยหลีกเลี่ยงรหัสดังกล่าวได้

ทำไมข้อมูลไม่ถูกรวมฉันไม่แน่ใจ - ฉันสงสัยว่าอย่างน้อยที่สุดในการสร้างการดีบักหากพวกเขาต้องการจริงๆพวกเขาสามารถหาได้ การถ่ายโอนข้อมูลผิดพลาดและเปิดใน WinDBG อาจช่วยได้


2

มีการสร้างข้อยกเว้นเป็นเครื่องมือในการส่งสัญญาณที่ไม่ร้ายแรงถึงเงื่อนไขขึ้นในสายโซ่ นั่นคือมันไม่ได้ถูกออกแบบมาเป็นเครื่องมือในการดีบั๊ก

หากมีข้อยกเว้นตัวชี้โมฆะเป็นเครื่องมือในการดีบักมันจะยกเลิกการดำเนินการของโปรแกรมทันทีที่จุดอนุญาตให้ดีบักเกอร์ในการเชื่อมต่อชี้ไปที่เส้นที่ถูกกล่าวหา สิ่งนี้จะให้ข้อมูลบริบททั้งหมดแก่โปรแกรมเมอร์ (ซึ่งค่อนข้างสวยสิ่งที่ Segfault เนื่องจากการเข้าถึงตัวชี้โมฆะทำใน C แม้ว่าจะ crudely เล็กน้อย)

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

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


1

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

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

ในเรื่องที่เกี่ยวกับObject reference not set to an instance of an objectรหัสไม่สามารถคาดเดาค่าที่เราอาจชอบ: นั่นเป็นหน้าที่ของเราในฐานะโปรแกรมเมอร์และมันก็หมายความว่าคุณได้ผ่านตัวแปรที่ไม่ได้กำหนดค่าเริ่มต้นแล้ว


คุณรู้หรือไม่ว่าผู้คนเคยเสนอข้ออ้างเช่นนี้เพื่อเขียนเป็นภาษาแอสเซมบลี และไม่ได้ใช้การรวบรวมขยะอัตโนมัติหรือไม่ และความสามารถในการทำเลขคณิต - ใน hex? "ภาระงานที่น่าเบื่อและน่าเบื่อบางครั้ง" เป็นวิธีที่เราอธิบายงานที่ควรเป็นไปโดยอัตโนมัติ
Spike0xff

0

เรียนรู้การใช้ดีบักเกอร์ นี่คือสิ่งที่ออกแบบมาอย่างแท้จริง กำหนดจุดพักบนวิธีการที่เป็นปัญหาและออกไป

เพียงก้าวผ่านโค้ดของคุณและดูว่าคุณค่าของตัวแปรทั้งหมดของคุณอยู่ที่จุดใด

แก้ไข: ตรงไปตรงมาฉันตกใจที่ยังไม่มีใครพูดถึงการใช้ดีบักเกอร์


2
ดังนั้นคุณกำลังบอกว่าข้อยกเว้นทั้งหมดสามารถทำซ้ำได้อย่างง่ายดายเมื่อใช้ดีบักเกอร์?
jgauffin

@ jgauffin ฉันกำลังบอกว่าคุณสามารถใช้ดีบักเกอร์เพื่อดูว่าทำไมข้อยกเว้นถูกส่งออกไปในรหัสโลกแห่งความจริงมากกว่าการทดสอบหน่วยสังเคราะห์ที่อาจไม่ได้ทดสอบรหัสอย่างเต็มรูปแบบหรือหน่วยทดสอบอาจมีข้อบกพร่อง พวกเขาจะพลาดข้อบกพร่องในรหัสจริง เครื่องมือดีบั๊กสำคัญกว่าเครื่องมืออื่น ๆ ที่ฉันนึกได้ (ยกเว้นบางอย่างเช่น Valgrind หรือ DTrace)
Cromulent

1
คุณกำลังบอกว่าเราสามารถเข้าถึงคอมพิวเตอร์ที่ล้มเหลวและการดีบักนั้นดีกว่าการทดสอบหน่วยเสมอ
jgauffin

@ jgauffin ฉันกำลังพูดว่าถ้าคุณมีทางเลือกให้ไปกับ debugger ตามความต้องการของเครื่องมืออื่น ๆ แน่นอนถ้าคุณไม่มีตัวเลือกนั้นมันเป็นบิตของผู้เริ่มต้น เห็นได้ชัดว่านี่ไม่ใช่กรณีของคำถามนี้ซึ่งเป็นเหตุผลที่ฉันตอบคำถามที่ฉันทำ หากคำถามนั้นเป็นอย่างไรคุณแก้ไขปัญหานี้ในคอมพิวเตอร์ไคลเอนต์ที่ไม่มีวิธีการดีบักแบบรีโมท (หรือการดีบักภายในเครื่อง) คำตอบของฉันจะแตกต่างกัน ดูเหมือนว่าคุณพยายามที่จะบิดเบือนคำตอบของฉันในรูปแบบที่ไม่มีความเกี่ยวข้องกับคำถามในมือ
Cromulent

0

หากคุณต้องการที่จะไปพร้อมกับคำตอบของ @ stijn และทำการตรวจสอบ null ในรหัสของคุณข้อมูลโค้ดนี้จะช่วยได้ นี่คือข้อมูลบางอย่างเกี่ยวกับโค้ด เมื่อคุณตั้งค่านี้แล้วให้พิมพ์argnullกดแท็บสองครั้งแล้วกรอกข้อมูลในช่องว่าง

<CodeSnippet Format="1.0.0">
  <Header>
    <Title>EnsureArgNotNull</Title>
    <Shortcut>argnull</Shortcut>
  </Header>
  <Snippet>
    <Declarations>
      <Literal>
        <ID>argument</ID>
        <ToolTip>The name of the argument that shouldn't be null</ToolTip>
        <Default>arg</Default>
      </Literal>
    </Declarations>
    <Code Language="CSharp">
      <![CDATA[if ($argument$ == null) throw new ArgumentNullException("$argument$");$end$]]>
    </Code>
  </Snippet>
</CodeSnippet>

หมายเหตุ: ตอนนี้ c # 6 มีnameofตัวอย่างข้อมูลของคุณthrow new ArgumentNullException(nameof($argument$))ซึ่งมีข้อดีของการไม่รวมค่าคงที่เวทย์มนตร์, การตรวจสอบโดยคอมไพเลอร์, และทำงานได้ดีขึ้นด้วยเครื่องมือการรี
แฟ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.