เหตุใดตัวแปรโลคอลจึงต้องมีการเริ่มต้น แต่ฟิลด์ทำไม่ได้?


140

ถ้าฉันสร้างบูลในชั้นเรียนของฉันbool checkมันก็เหมือนกับค่าเริ่มต้นที่เป็นเท็จ

เมื่อฉันสร้างบูลเดียวกันภายในเมธอดของฉันbool check(แทนที่จะเป็นคลาส) ฉันได้รับข้อผิดพลาด "ใช้การตรวจสอบตัวแปรโลคอลที่ไม่ได้กำหนด" ทำไม?


ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
Martijn Pieters

14
คำถามที่คลุมเครือ "เพราะข้อมูลจำเพาะบอกว่า" จะเป็นคำตอบที่ยอมรับได้หรือไม่
Eric Lippert

4
เพราะนั่นคือวิธีที่มันทำใน Java เมื่อพวกเขาคัดลอกมัน : P
Alvin Thompson

คำตอบ:


177

Yuval และคำตอบของดาวิดนั้นถูกต้องแล้ว สรุป:

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

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

ออกครั้งแรกสำหรับตัวแปรใด ๆ ในท้องถิ่นหรือมิฉะนั้นมันเป็นไปไม่ได้ในทางปฏิบัติเพื่อตรวจสอบว่าไม่ว่าจะเป็นตัวแปรที่ได้รับมอบหมายหรือยังไม่ได้กำหนด พิจารณา:

bool x;
if (M()) x = true;
Console.WriteLine(x);

คำถาม "ได้รับมอบหมาย x?" เทียบเท่ากับ "M () ส่งคืนจริงหรือไม่" ทีนี้สมมติว่า M () คืนค่าเป็นจริงถ้าทฤษฎีบทสุดท้ายของแฟร์มาต์เป็นจริงสำหรับจำนวนเต็มทั้งหมดที่น้อยกว่าสิบเอ็ดล้านล้านและเป็นเท็จ เพื่อพิจารณาว่า x ได้รับมอบหมายอย่างแน่นอนผู้แปลต้องสร้างหลักฐานบททฤษฎีบทสุดท้ายของแฟร์มาต์ คอมไพเลอร์ไม่ใช่สมาร์ทตัวนั้น

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

bool x;
if (N() * 0 == 0) x = true;
Console.WriteLine(x);

สมมติว่า N () ส่งคืนเลขจำนวนเต็ม คุณและฉันรู้ว่า N () * 0 จะเป็น 0 แต่คอมไพเลอร์ไม่ทราบว่า (หมายเหตุ: ที่ C # 2.0 คอมไพเลอร์ไม่ทราบว่า แต่ฉันออกเพิ่มประสิทธิภาพที่เป็นสเปคไม่ได้บอกว่าคอมไพเลอร์รู้ว่า.)

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

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

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

โดยพื้นฐานแล้ว initializer สำหรับฟิลด์อาจอยู่ที่ใดก็ได้ในโปรแกรมทั้งหมดรวมถึงวิธีเสมือนภายในที่จะประกาศในไลบรารีที่ยังไม่ได้เขียน :

// Library written by BarCorp
public abstract class Bar
{
    // Derived class is responsible for initializing x.
    protected int x;
    protected abstract void InitializeX(); 
    public void M() 
    { 
       InitializeX();
       Console.WriteLine(x); 
    }
}

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

สมมติว่าห้องสมุดนี้ถูกกฎหมาย ถ้า FooCorp เขียน

public class Foo : Bar
{
    protected override void InitializeX() { } 
}

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

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

ตอนนี้ commenter แนะนำ "ต้องการให้ตัวสร้างเริ่มต้นทุกฟิลด์" นั่นไม่ใช่ความคิดที่เลว ในความเป็นจริงมันเป็นความคิดที่ไม่เลวที่C # มีคุณสมบัติสำหรับ structsอยู่แล้ว ตัวสร้าง struct จำเป็นต้องกำหนดเขตข้อมูลทั้งหมดอย่างแน่นอนตามเวลาที่ ctor ส่งคืนตามปกติ ตัวสร้างเริ่มต้นเริ่มต้นเขตข้อมูลทั้งหมดเป็นค่าเริ่มต้นของพวกเขา

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

จอห์นแนะนำเพียงแค่ห้ามวิธีการโทรใน ctor ก่อนที่จะมีการเริ่มต้นเขตข้อมูล ดังนั้นสรุปตัวเลือกของเราคือ:

  • ทำให้การเขียนโปรแกรมสำนวนทั่วไปปลอดภัยและใช้ผิดกฎหมาย
  • ทำการวิเคราะห์ทั้งโปรแกรมที่มีราคาแพงซึ่งทำให้การรวบรวมใช้เวลาหลายชั่วโมงเพื่อหาข้อบกพร่องที่อาจไม่มี
  • พึ่งพาการกำหนดค่าเริ่มต้นอัตโนมัติเป็นค่าเริ่มต้น

ทีมออกแบบเลือกตัวเลือกที่สาม


1
คำตอบที่ดีตามปกติ ฉันมีคำถาม: ทำไมไม่กำหนดค่าเริ่มต้นโดยอัตโนมัติให้กับตัวแปรท้องถิ่นเช่นกัน? กล่าวอีกนัยหนึ่งทำไมไม่ทำตัวbool x;เทียบเท่ากับbool x = false; วิธีใด ๆ
durron597

8
@ durron597: เนื่องจากประสบการณ์พบว่าการลืมการกำหนดค่าให้กับ local อาจเป็นจุดบกพร่อง หากอาจเป็นข้อผิดพลาดและราคาถูกและตรวจจับได้ง่ายมีแรงจูงใจที่ดีที่จะทำให้พฤติกรรมนั้นผิดกฎหมายหรือเป็นการเตือน
Eric Lippert

27

เมื่อฉันสร้างบูลเดียวกันภายในวิธีการตรวจสอบบูล (แทนที่จะเป็นคลาส) ฉันได้รับข้อผิดพลาด "ใช้การตรวจสอบตัวแปรภายในที่ไม่ได้กำหนด" ทำไม?

เพราะคอมไพเลอร์พยายามที่จะป้องกันไม่ให้คุณทำผิดพลาด

การกำหนดค่าเริ่มต้นให้กับตัวแปรของคุณเพื่อfalseเปลี่ยนแปลงอะไรในเส้นทางการดำเนินการนี้โดยเฉพาะหรือไม่ อาจไม่ได้พิจารณาว่าdefault(bool)เป็นเท็จ แต่ก็บังคับให้คุณตระหนักว่าสิ่งนี้เกิดขึ้น สภาพแวดล้อม. NET ป้องกันไม่ให้คุณเข้าถึง "หน่วยความจำขยะ" เนื่องจากมันจะเริ่มต้นค่าใด ๆ ให้เป็นค่าเริ่มต้น แต่ถึงกระนั้นลองจินตนาการว่านี่เป็นประเภทอ้างอิงและคุณจะส่งค่าที่ไม่ได้กำหนดค่าเริ่มต้น (null) ไปยังวิธีที่คาดหวังว่าไม่ใช่ค่าว่างและรับ NRE เมื่อใช้งานจริง คอมไพเลอร์พยายามเพียงแค่ป้องกันไม่ให้ยอมรับความจริงที่ว่าบางครั้งอาจส่งผลให้เกิดbool b = falseคำสั่ง

Eric Lippert พูดถึงสิ่งนี้ในบล็อกโพสต์ :

เหตุผลที่เราต้องการทำให้สิ่งผิดกฎหมายนี้ไม่ได้เป็นอย่างที่หลายคนเชื่อเพราะตัวแปรท้องถิ่นกำลังเริ่มต้นกับขยะและเราต้องการปกป้องคุณจากขยะ ในความเป็นจริงเราจะเริ่มต้นคนในพื้นที่โดยอัตโนมัติเป็นค่าเริ่มต้น (แม้ว่าภาษาการเขียนโปรแกรม C และ C ++ ไม่ได้และจะช่วยให้คุณอ่านขยะจากเครื่องที่ไม่มีการกำหนดค่าภายในเครื่อง) แต่เป็นเพราะการมีอยู่ของรหัสเส้นทางนั้นอาจเป็นข้อผิดพลาดและเราต้องการทิ้งคุณไว้ใน หลุมคุณภาพ คุณควรทำงานอย่างหนักเพื่อเขียนบั๊กนั้น

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


"ลองนึกภาพว่านี่เป็นประเภทอ้างอิงและคุณจะส่งวัตถุที่ไม่ได้กำหนดค่าเริ่มต้นไปยังวิธีที่คาดว่าจะมีค่าเริ่มต้น" คุณหมายถึง: "จินตนาการว่านี่เป็นประเภทอ้างอิงและคุณผ่านค่าเริ่มต้น (null) แทนการอ้างอิง วัตถุ"?
Deduplicator

@Deduplicator ใช่ วิธีการที่คาดหวังค่าที่ไม่ใช่ศูนย์ แก้ไขส่วนนั้น หวังว่าจะชัดเจนขึ้นในขณะนี้
Yuval Itzchakov

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

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

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

25

เหตุใดตัวแปรโลคอลจึงต้องมีการเริ่มต้น แต่ฟิลด์ทำไม่ได้?

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

ทำไมตัวแปรท้องถิ่นถึงต้องการการเริ่มต้น

นี่คือไม่มากไปกว่าการตัดสินใจการออกแบบของภาษา C # เป็นอธิบายโดยเอริค Lippert สภาพแวดล้อม CLR และ. NET ไม่จำเป็นต้องใช้ ยกตัวอย่างเช่น VB.NET จะคอมไพล์ได้ดีกับตัวแปรเฉพาะที่ไม่ได้กำหนดค่าเริ่มต้นและในความเป็นจริง CLR จะกำหนดค่าเริ่มต้นให้กับตัวแปรทั้งหมดที่ไม่ใช่ค่าเริ่มต้น

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

เหตุใดเขตข้อมูลจึงไม่ต้องการการเริ่มต้น

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

ประเภทคอลเลกชันคืออะไร

การบังคับใช้การกำหนดค่าเริ่มต้นของตัวแปรโลคอล C # นั้นมี จำกัด ซึ่งมักจะทำให้ผู้พัฒนาไม่สนใจ พิจารณารหัสสี่บรรทัดต่อไปนี้:

string str;
var len1 = str.Length;
var array = new string[10];
var len2 = array[0].Length;

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


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

@JohnKugelman พิจารณากรณีที่เรียบง่ายของและวิธีการpublic interface I1 { string str {get;set;} } int f(I1 value) { return value.str.Length; }หากสิ่งนี้มีอยู่ในไลบรารีคอมไพเลอร์ไม่สามารถรู้ได้ว่าไลบรารีนั้นจะเชื่อมโยงกับสิ่งนั้นหรือไม่ดังนั้นsetจะมีการเรียกใช้ก่อนหน้าgetนั้นหรือไม่ฟิลด์ข้อมูลสำรองอาจไม่ถูกเตรียมใช้งานอย่างชัดเจน แต่จะต้องรวบรวมรหัสดังกล่าว
David Arno

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

@JohnKugelman: ฉันจะโพสต์คำตอบเกี่ยวกับปัญหาที่คุณแจ้ง
Eric Lippert

4
นั่นไม่ยุติธรรม. เรากำลังพยายามที่จะมีความขัดแย้งที่นี่!
John Kugelman

10

คำตอบที่ดีด้านบน แต่ฉันคิดว่าฉันโพสต์คำตอบที่ง่ายกว่า / สั้นกว่ามากสำหรับคนที่ขี้เกียจอ่านอันยาวเหยียด (เหมือนตัวเอง)

ชั้น

class Foo {
    private string Boo;
    public Foo() { /** bla bla bla **/ }
    public string DoSomething() { return Boo; }
}

คุณสมบัติBooอาจมีหรือไม่มีการเตรียมใช้งานในตัวสร้าง ดังนั้นเมื่อพบว่าreturn Boo;มันไม่คิดว่ามันถูกเริ่มต้น มันเพียงแค่ระงับข้อผิดพลาด

ฟังก์ชัน

public string Foo() {
   string Boo;
   return Boo; // triggers error
}

{ }ตัวอักษรกำหนดขอบเขตของบล็อกของรหัสที่ คอมไพเลอร์เดินตามกิ่งก้านของ{ }บล็อกเหล่านี้เพื่อติดตามสิ่งของ สามารถบอกได้อย่างง่ายดายBooว่าไม่ได้เริ่มต้น ข้อผิดพลาดจะถูกเรียกใช้แล้ว

ทำไมข้อผิดพลาดอยู่?

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

public string Foo() {
   string Boo;
   /* bla bla bla */
   if(Boo == null) {
      return "";
   }
   return Boo;
}

จากคู่มือ:

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

การอ้างอิง: https://msdn.microsoft.com/en-us/library/4y7h161d.aspx

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