เหตุใดฉันจึงประกาศตัวแปรลูกที่มีชื่อเดียวกันกับตัวแปรในขอบเขตพาเรนต์


23

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

var x = 1;
Action<int> myAction = (x) => { Console.WriteLine(x); };

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

ทำไมรหัสนี้ใช้งานได้? ฉันใช้ C # 8 กับ Visual Studio 2019


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

6
อาจเป็นไปได้ว่าที่นี่จะไม่รวบรวมเมื่อกำหนดเป้าหมาย C # 7.3 ดังนั้นดูเหมือนว่าจะไม่รวมอยู่ใน C # 8
Jonathon Chase

รหัสในคำถามที่เชื่อมโยงยังรวบรวมปรับในsharplab นี่อาจเป็นการเปลี่ยนแปลงล่าสุด
Lasse V. Karlsen

2
พบคนล่อ (ไม่มีคำตอบ): stackoverflow.com/questions/58639477/…
bolov

คำตอบ:


26

ทำไมรหัสนี้ใช้งานได้? ฉันใช้ C # 8 กับ Visual Studio 2019

คุณได้ตอบคำถามของคุณเอง! เป็นเพราะคุณกำลังใช้ C # 8

กฎจาก C # 1 ถึง 7 คือ: ชื่อแบบง่ายไม่สามารถใช้เพื่อหมายถึงสองสิ่งที่แตกต่างในขอบเขตท้องถิ่นเดียวกัน (กฎจริงนั้นซับซ้อนกว่าเล็กน้อย แต่อธิบายว่าน่าเบื่อได้อย่างไรดูรายละเอียด C # สำหรับรายละเอียด)

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

class C 
{
  int x;
  void M()
  {
    x = 123;
    if (whatever)
    {
      int x = 356;
      ...

และตอนนี้เรามีสถานการณ์ที่ภายในร่างกายของM, xวิธีการทั้งสองและท้องถิ่นthis.xx

แม้ว่าจะมีเจตนาดี แต่ก็มีปัญหามากมายเกี่ยวกับกฎนี้:

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

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

ทีม C # ตัดสินใจสำหรับ C # 8 ว่ากฎทั้งหมดก่อให้เกิดความสับสนมากกว่าการป้องกันและกฎนั้นถูกยกเลิกจากภาษา (ขอบคุณ Jonathon Chase สำหรับการพิจารณาว่าเมื่อใดจะเกิดการเกษียณอายุ)

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

https://ericlippert.com/2009/11/02/simple-names-are-not-so-simple/

https://ericlippert.com/2009/11/05/simple-names-are-not-so-simple-part-two/

https://ericlippert.com/2014/09/25/confusing-errors-for-a-confusing-feature-part-one/

https://ericlippert.com/2014/09/29/confusing-errors-for-a-confusing-feature-part-two/

https://ericlippert.com/2014/10/03/confusing-errors-for-a-confusing-feature-part-three/

ในตอนท้ายของส่วนที่สามฉันสังเกตเห็นว่ามีการโต้ตอบระหว่างคุณลักษณะนี้และคุณลักษณะ "สีสี" - นั่นคือคุณสมบัติที่ช่วยให้:

class C
{
  Color Color { get; set; }
  void M()
  {
    Color = Color.Red;
  }
}

ที่นี่เราได้ใช้ชื่อง่ายๆColorในการอ้างถึงทั้งสองthis.ColorและประเภทแจกแจงColor; ตามข้อกำหนดการอ่านที่เข้มงวดนี้ควรเป็นข้อผิดพลาด แต่ในกรณีนี้ข้อมูลจำเพาะผิดและความตั้งใจที่จะอนุญาตให้ใช้งานได้เนื่องจากรหัสนี้ไม่คลุมเครือและจะทำให้ผู้พัฒนาเปลี่ยนแปลง

ฉันไม่เคยเขียนบทความที่อธิบายถึงการโต้ตอบแปลก ๆ ทั้งหมดระหว่างกฎทั้งสองนี้และมันจะไม่มีจุดหมายสักหน่อยที่จะทำตอนนี้!


รหัสในคำถามล้มเหลวในการคอมไพล์สำหรับ C # 6, 7, 7.1, 7.2, และ 7.3 โดยให้ "CS0136: โลคัลหรือพารามิเตอร์ชื่อ 'x' ไม่สามารถประกาศในขอบเขตนี้เนื่องจากชื่อ ... " ดูเหมือนว่ากฎจะถูกบังคับใช้จนถึง C # 8
Jonathon Chase

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