Döngüde bildirilen değişken yerel değişken mi?


133

C # uzun süredir kullanıyorum ama aşağıdakileri hiç fark etmedim:

 public static void Main()
 {
     for (int i = 0; i < 5; i++)
     {

     }

     int i = 4;  //cannot declare as 'i' is declared in child scope                
     int A = i;  //cannot assign as 'i' does not exist in this context
 }

Öyleyse neden bu adla bir değişken bildirmeme izin vermiyorsa, for bloğunun dışında 'i' değerini kullanamıyorum?

Bir for-loop tarafından kullanılan yineleyici değişkeninin sadece kapsamı içinde geçerli olduğunu düşündüm.


8
Dış blok kapsamı for döngüsü kapsamını içerdiğinden
V4Vendetta

3
Bunu düşünmenin bir yolu (ve bazı kod stili kılavuzları, özellikle dinamik yazılan diller için bunu gerektirir), bir kapsamda bildirilen tüm değişkenlerin bu kapsamın başında bildirilebilmesidir, yani işleviniz yeniden yazılabilirint i, A; for(int i = 0; i < 5; i++){ } i=4; A=i
Keith

2
@ V4Vendetta: Tam tersi. İç blok üst bloğa kara bir kutudur.
Sebastian Mach

4
Bunun neden mümkün olmadığının teknik nedenlerinin yanı sıra, bu (ya da bunun bir varyantı) neden yapılması mantıklı bir şey olsun ki ?! Açıkçası farklı bir amaca hizmet eder, bu yüzden ona farklı bir isim verin.
Dave

Bunu hiç fark etmedim ama tamamen şaşkın bir kısıtlama gibi görünüyor.
alan2burada

Yanıtlar:


119

Hem for döngüsünde hem de for döngüsünün dışında aynı ada sahip bir değişken tanımlamanıza izin verilmemesinin nedeni, dış kapsamdaki değişkenlerin iç kapsamda geçerli olmasıdır. Eğer buna izin verilirse for-loop içinde iki 'i' değişkeni olacaktır.

Bkz: MSDN Kapsamları

özellikle:

Yerel değişken bildirimlerinde bildirilen bir yerel değişkenin kapsamı (Bölüm 8.5.1), bildirimin gerçekleştiği bloktur.

ve

For ifadesinin for-başlatıcısında bildirilen bir yerel değişkenin kapsamı (Bölüm 8.8.3) for for başlatıcısı, for-koşulu, for-yineleyici ve for ifadesinin içerdiği ifadesidir.

Ve ayrıca: Yerel değişken bildirimleri (C # spesifikasyonunun 8.5.1 Bölümü)

özellikle:

Yerel-değişken-bildirimde bildirilen bir yerel değişkenin kapsamı, bildirimin gerçekleştiği bloktur. Yerel değişkenin yerel-değişken-bildiricisinden önce gelen metinsel konumdaki yerel bir değişkene başvurmak bir hatadır. Bir yerel değişken kapsamında, aynı ada sahip başka bir yerel değişken veya sabit bildirmek derleme zamanı hatasıdır.

(Vurgu benim.)

Bu i, for-loop'unuzun içinin kapsamının for-loop olduğu anlamına gelir . Oysa ifor-loop'unuzun dışının kapsamı tüm ana yöntem artı for-loop'tur. Yani iki kez tekrar edersinizi , döngü içinde yukarıdakilere göre geçersiz olan .

Yapmanıza izin verilmemesinin int A = i;nedeni int i, yalnızca fordöngü içinde kullanım için kapsamlandırılmış olmasıdır . Böylece fordöngü dışında artık erişilemez .

Gördüğünüz gibi her iki konu da kapsamın bir sonucudur; ilk sayı ( int i = 4;) döngü kapsamı iiçinde iki değişkenle sonuçlanır for. Oysa int A = i;kapsam dışı bir değişkene erişim ile sonuçlanır.

Bunun yerine, itüm yöntemin kapsamına alınacağının bildirilmesi ve daha sonra hem yöntemde hem de for-döngüsü kapsamında kullanılması bildirilir. Bu, her iki kuralı da ihlal etmekten kaçınacaktır.

public static void Main()
{
    int i;

    for (i = 0; i < 5; i++)
    {

    }

    // 'i' is only declared in the method scope now, 
    // no longer in the child scope -> valid.
    i = 4;

    // 'i' is declared in the method's scope -> valid. 
    int A = i;
}

DÜZENLE :

C # derleyicisi elbette bu kodun oldukça geçerli bir şekilde derlenmesine izin verecek şekilde değiştirilebilir. Sonuçta bu geçerlidir:

for (int i = 0; i < 5; i++)
{
    Console.WriteLine(i);
}

for (int i = 5; i > 0; i--)
{
    Console.WriteLine(i);
}

Ancak, kodun okunabilmesi ve sürdürülebilmesi için aşağıdaki gibi bir kod yazabilmeniz gerçekten yararlı olacaktır:

public static void Main()
{
    int i = 4;

    for (int i = 0; i < 5; i++)
    {
        Console.WriteLine(i);
    }

    for (int i = 5; i > 0; i--)
    {
        Console.WriteLine(i);
    }

    Console.WriteLine(i);
}

Burada hata olasılığını düşünün, son i0 veya 4 çıktı mı? Şimdi bu çok küçük bir örnek, takip edilmesi ve izlenmesi oldukça kolay ama dış görünüşü ifarklı bir isimle ilan etmekten kesinlikle daha az bakım yapılabilir ve okunabilir .

NB:

C # 'ın kapsam belirleme kurallarının C ++' ın kapsam belirleme kurallarından farklı olduğunu lütfen unutmayın . C ++ 'da değişkenler sadece bildirildikleri yerden bloğun sonuna kadar kapsamlıdır. Bu da kodunuzu C ++ 'da geçerli bir yapı yapar.


Mantıklı, ben iç ideğişken olsa hata gerektiğini düşünüyorum ; bu benim için daha açık görünüyor.
George Duckett

Ama kapsam komut ve {} için mi? Yoksa ebeveyn {} anlamına mı geliyor?
John V

2
Eğer for ifadesinden SONRA 'A' ilan edersem, daha sonra bildirildiği gibi for döngüsünde geçerli değildir. Bu yüzden aynı adın neden kullanılamadığını anlamıyorum.
John V

9
Belki de bu cevap, bütünlük açısından, bu durumda C # kapsam kurallarının C ++ kurallarından farklı olduğuna işaret etmelidir . C ++ 'da, değişkenler yalnızca bloğun sonuna kadar bildirildikleri yerdedir (bkz. Msdn.microsoft.com/en-us/library/b7kfh662(v=vs.80).aspx ).
AAT

2
Java'nın C ++ ve C # arasında bir ara yaklaşım aldığını unutmayın: OP'nin örneği Java'da geçerli olacaktır, ancak dış itanım for döngüsünden önce taşındıysa, iç itanım geçersiz olarak işaretlenir.
Nicola Musatti

29

J.Kommer'in cevabı doğrudur: kısaca, aynı değişkenin yereline sahip başka bir yerel değişken bildirim alanı ile çakışan bir yerel değişken bildirim alanında yerel bir değişkenin bildirilmesi yasadışıdır .

Burada da ihlal edilen ek bir C # kuralı vardır. Ek kural, üst üste binen iki farklı yerel değişken bildirim boşluğundaki iki farklı varlığı ifade etmek için basit bir adın kullanılmasının yasadışı olmasıdır. Yani sadece örnek yasadışı değil, bu da yasadışı:

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

Çünkü şimdi "x" basit adı, "y" yerel değişken bildirim alanı içinde iki farklı anlama gelir: "this.x" ve yerel "x".

Bu sorunların daha fazla analizi için http://blogs.msdn.com/b/ericlippert/archive/tags/simple+names/ adresine bakın .


2
Ayrıca, değişiklik yaptığınızda örnek kodunuzun derleneceğini not etmek ilginçtirint y = this.x;
Phil

4
@Phil: Doğru. basit bir isimthis.x değildir .
Eric Lippert

13

Döngüden isonra yöntemin içinde bildirmenin ve kullanmanın bir yolu vardır :

static void Main()
{
    for (int i = 0; i < 5; i++)
    {

    }

    {
        int i = 4;
        int A = i;
    }
}

Java ile bunu yapabilirsiniz (emin değilim C kökenli olabilir). Değişken bir isim uğruna elbette biraz dağınık.


Evet, bu C / C ++ kaynaklıdır.
Branko Dimitrijevic

1
Bunu daha önce bilmiyordum. Düzgün dil özelliği!

@MathiasLykkegaardLorenzen evet
Chris S

7

Döngünüzden i önce bildirdiyseniz , fordöngü içinde bildirmenin geçerli olması gerektiğini düşünüyor musunuz?

Hayır, çünkü o zaman ikisinin kapsamı örtüşecektir.

Yapamamaya gelince, bunun int A=i;nedeni isadece fordöngüde olması gerektiği gibi olmasıdır.


7

J.Kommer'in cevabına ek olarak (+1 btw). NET kapsamı standardında bu var:

block Bir blok yapısı içinde If ifadesi gibi bir değişken bildirirseniz, bu değişkenin kapsamı yalnızca bloğun sonuna kadardır. Kullanım ömrü, işlem sona erene kadardır.

Yordam Bir yordam içinde ancak herhangi bir If ifadesinin dışında bir değişken bildirirseniz, kapsam End Sub veya End İşlevine kadar olur. Değişkenin ömrü prosedürler bitene kadar sürer.

Bu nedenle, for döngüsü başlığı içinde bildirilen int, yalnızca for döngüsü bloğu sırasında kapsam dahilinde olacaktır, ancakMain() kod tamamlanıncaya kadar kullanım ömrü devam eder .


5

Bunu düşünmenin en kolay yolu, I'nin dış bildirimini döngünün üstüne taşımaktır. O zaman belli olmalı.

Her iki şekilde de aynı kapsamdır, bu nedenle yapılamaz.


4

Ayrıca C # 'ın kuralları kesinlikle programlama açısından pek çok zaman gerekli değildir, ancak kodunuzu temiz ve okunabilir tutmak için vardır.

örneğin, döngüden sonra tanımlarsanız tamam, ancak kodunuzu okuyan ve tanım satırını kaçıran kişi, döngü değişkeniyle ilgili olduğunu düşünebilir.


2

Kommer'in cevabı teknik olarak doğrudur. Canlı bir kör ekran metaforu ile açıklayayım.

For-blok ile çevreleyen dış blok arasında tek yönlü kör ekran vardır, böylece for-blok içindeki kod dış kodu görebilir, ancak dış bloktaki kod içerideki kodu göremez.

Dış kod içeride göremediğinden içeride bildirilen hiçbir şeyi kullanamaz. Ancak for-bloğundaki kod hem içeride hem de dışarıda görebildiğinden, her iki yerde de bildirilen bir değişken adıyla açık bir şekilde kullanılamaz.

Yani ya onu görmüyorsunuz, ya da C #!


0

Eğer aynı şekilde ona bak olabilir bir beyan intbir de usingbloğu:

using (int i = 0) {
  // i is in scope here
}
// here, i is out of scope

Ancak, intuygulamadığı için IDisposablebu yapılamaz. Bununla birlikte, bir intdeğişkenin özel bir kapsama nasıl yerleştirildiğini görselleştirmesine yardımcı olabilir .

Başka bir yol,

if (true) {
  int i = 0;
  // i is in scope here
}
// here, i is out of scope

Umarım bu neler olup bittiğini görselleştirmeye yardımcı olur.

Döngünün intiçinden bildirmek forkodu güzel ve sıkı tutar olarak bu özelliği gerçekten seviyorum .


O NE LAN? Bir NEG'yi etiketleyecekseniz, nedenlerini söyleyecek topları toplayın.
jp2code

usingAnlambilimci değil ve semantikler oldukça farklı olsa da karşılaştırmanın oldukça iyi olduğunu düşünüyorum (belki de bu yüzden aşağı indirildi). Bunun if (true)gereksiz olduğunu unutmayın . Çıkartın ve bir kapsam bloğunuz var.
Abel
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.