Üst kapsamdaki bir değişkenle aynı ada sahip bir alt değişkeni neden bildirebilirim?


23

Yakın zamanda, aynı adı taşıyan bir değişkene sahip bir işlev içinde bildirilen bir eylemin parametresi olarak bir değişken adını yanlışlıkla yeniden kullandığım son zamanlarda bazı kodlar yazdım. Örneğin:

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

Ben çoğaltma benekli, ben kod derlenmiş ve mükemmel koştu görmek için sürpriz oldu, hangi ben C # kapsamı hakkında bilmek ne dayalı beklediğiniz davranış değildir. Bazı hızlı Googling bu benzer bir kod şikayet SO soruları döndü gelmez gibi bir hatanın oluşmasına Lambda Kapsam netleştirilmesi . (Çalıştığından emin olmak için bu örnek kodu IDE'ye yapıştırdım, sadece emin olmak için; mükemmel çalışıyor.) Ayrıca, Visual Studio'da Yeniden Adlandır iletişim kutusuna girdiğimde, ilki xbir ad çakışması olarak vurgulanır.

Bu kod neden çalışıyor? Visual Studio 2019 ile C # 8 kullanıyorum.


1
Lambda, derleyici tarafından oluşturulan bir sınıftaki bir yönteme taşınır ve böylece xbu yönteme ait tüm parametre kapsam dışına taşınır. Örnek için sharplab'a bakınız .
Lasse V. Karlsen

6
Burada, C # 7.3'ü hedeflerken derlenmeyeceğini belirtmek gerekir, bu nedenle bu C # 8'e özel görünüyor.
Jonathon Chase

Bağlantılı sorudaki kod ayrıca sharplab'da iyi derler . Bu yeni bir değişiklik olabilir.
Lasse V. Karlsen

2
bir dupe buldum (
cevapsız

Yanıtlar:


26

Bu kod neden çalışıyor? Visual Studio 2019 ile C # 8 kullanıyorum.

Kendi sorunuzu cevapladınız! Çünkü C # 8 kullanıyorsunuz.

C # 1'den 7'ye kadar olan kural şuydu: Basit bir ad, aynı yerel kapsamda iki farklı anlama gelmek için kullanılamaz. (Gerçek kural bundan biraz daha karmaşıktı, ancak nasıl sıkıcı olduğunu açıklıyor; ayrıntılar için C # spesifikasyonuna bakın.)

Bu kuralın amacı, örneğinizde bahsettiğiniz, yerelin anlamı hakkında karıştırılmanın çok kolay olduğu bir tür durumu önlemekti. Özellikle, bu kural aşağıdaki gibi karışıklıkları önlemek için tasarlanmıştır:

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

Ve şimdi vücudunda özel bir durum nerede var M, xaraçlar hem this.xyerel x.

İyi niyetli olmasına rağmen, bu kuralla ilgili bir takım sorunlar vardı:

  • Şartnameye uygulanmadı. Basit bir adın hem tür hem de özellik olarak kullanılabileceği durumlar vardı, ancak hata algılama mantığı hatalı olduğu için bunlar her zaman hata olarak işaretlenmedi. (Aşağıya bakınız)
  • Hata mesajları kafa karıştırıcı bir şekilde ifade edildi ve tutarsız bir şekilde rapor edildi. Bu durum için birden fazla farklı hata mesajı vardı. Suçluyu tutarsız bir şekilde tespit ettiler; yani bazen içsel kullanım, bazen dışsal ve bazen de kafa karıştırıcı olurdu.

Roslyn'de bunu çözmek için bir çaba harcadım; Bazı yeni hata mesajları ekledim ve eskilerini hatanın nerede bildirildiği konusunda tutarlı hale getirdim. Ancak, bu çaba çok az, çok geç oldu.

C # ekibi C # 8 için tüm kuralın önlediğinden daha fazla karışıklığa neden olduğuna karar verdi ve kural dilden emekliye ayrıldı. (Emekliliğin ne zaman gerçekleştiğini belirlediği için Jonathon Chase'e teşekkür ederiz.)

Bu sorunun tarihini ve nasıl düzeltmeye çalıştığımı öğrenmek istiyorsanız, bu konuda yazdığım şu makalelere bakın:

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/

Üçüncü bölümün sonunda, bu özellik ile "Renk Rengi" özelliği - yani aşağıdakilere izin veren özellik arasında bir etkileşim olduğunu belirttim:

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

Burada Colorhem basit numarayı hem this.Colorde numaralandırılmış türü ifade etmek için kullandık Color; spesifikasyonun sıkı bir şekilde okunmasına göre bu bir hata olmalıdır, ancak bu durumda spesifikasyon yanlıştı ve niyet izin vermekti, çünkü bu kod açık ve geliştiricinin değiştirmesini sağlamak için can sıkıcı olurdu.

Bu iki kural arasındaki garip etkileşimleri açıklayan bir makaleyi hiç yazmadım ve şimdi bunu yapmak biraz anlamsız olurdu!


Söz konusu kod C # 6, 7, 7.1, 7.2 ve 7.3 için derlenemiyor ve "CS0136: 'x' adında bir yerel veya parametre bu kapsamda bildirilemez çünkü bu ad ...". Kural C # 8'e kadar hala zorlanıyor gibi görünüyor.
Jonathon Chase

@JonathonChase: Teşekkürler!
Eric Lippert
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.