Variabel yang dideklarasikan dalam for-loop adalah variabel lokal?

133

Saya telah menggunakan C # untuk waktu yang cukup lama tetapi tidak pernah menyadari hal berikut:

 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
 }

Jadi mengapa saya tidak bisa menggunakan nilai 'i' di luar blok for jika itu tidak memungkinkan saya untuk mendeklarasikan variabel dengan nama ini?

Saya berpikir bahwa variabel iterator yang digunakan oleh for-loop hanya valid dalam cakupannya.

John V
sumber
8
Karena ruang lingkup blok luar mencakup ruang lingkup untuk loop
V4Vendetta
3
Salah satu cara saya memikirkannya (dan beberapa panduan gaya kode memerlukan ini, terutama untuk bahasa yang diketik secara dinamis) adalah bahwa semua variabel yang dideklarasikan dalam ruang lingkup dapat dideklarasikan pada awal ruang lingkup itu, yang berarti bahwa fungsi Anda dapat ditulis ulangint i, A; for(int i = 0; i < 5; i++){ } i=4; A=i
Keith
2
@ V4Vendetta: Ini lebih sebaliknya. Blok dalam adalah kotak hitam untuk blok induk.
Sebastian Mach
4
Selain alasan teknis mengapa ini tidak mungkin, mengapa ini (atau varian dari ini) akan menjadi hal yang masuk akal untuk dilakukan ?! Itu jelas melayani tujuan yang berbeda, jadi berikan nama yang berbeda.
Dave
Saya tidak menyadari hal ini sama sekali tetapi sepertinya itu adalah pembatasan yang sangat bodoh.
alan2 di sini

Jawaban:

119

Alasan Anda tidak diizinkan untuk mendefinisikan variabel dengan nama yang sama di kedua for-loop maupun di luar for-loop adalah karena variabel dalam lingkup luar valid dalam lingkup dalam. Berarti akan ada dua variabel 'i' di dalam for-loop jika ini diizinkan.

Lihat: Ruang Lingkup MSDN

Secara khusus:

Cakupan variabel lokal yang dideklarasikan dalam deklarasi variabel lokal (Bagian 8.5.1) adalah blok tempat deklarasi terjadi.

dan

Lingkup variabel lokal yang dideklarasikan dalam for-initializer dari statement for (Bagian 8.8.3) adalah for-initializer, for-condition, for-iterator, dan pernyataan yang terkandung dari statement for.

Dan juga: Deklarasi variabel lokal (Bagian 8.5.1 dari spesifikasi C #)

Secara khusus:

Cakupan variabel lokal yang dideklarasikan dalam deklarasi variabel lokal adalah blok tempat deklarasi terjadi. Adalah kesalahan untuk merujuk ke variabel lokal dalam posisi tekstual yang mendahului deklarator variabel lokal dari variabel lokal. Dalam lingkup variabel lokal, merupakan kesalahan waktu kompilasi untuk mendeklarasikan variabel lokal lain atau konstanta dengan nama yang sama.

(Penekanan milikku.)

Yang berarti bahwa ruang lingkup di idalam for-loop Anda, adalah untuk-loop. Sedangkan ruang lingkup bagian iluar for-loop Anda adalah seluruh metode utama plus for-loop. Berarti Anda akan memiliki dua kejadian idi dalam loop yang tidak valid sesuai dengan di atas.

Alasan mengapa Anda tidak diizinkan melakukannya int A = i;adalah karena int ihanya dicakup untuk digunakan dalam forloop. Dengan demikian tidak lagi dapat diakses di luar forloop.

Seperti yang Anda lihat, kedua masalah ini adalah hasil pelingkupan; masalah pertama ( int i = 4;) akan menghasilkan dua ivariabel dalam forlingkup loop. Sedangkan int A = i;akan menghasilkan akses ke variabel yang berada di luar cakupan.

Apa yang bisa Anda lakukan adalah mendeklarasikan iuntuk mencakup seluruh metode, dan kemudian menggunakannya baik dalam metode maupun lingkup for-loop. Ini akan menghindari melanggar salah satu aturan.

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;
}

EDIT :

Kompiler C # tentu saja dapat diubah untuk memungkinkan kode ini dikompilasi dengan cukup valid. Setelah semua ini valid:

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

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

Tetapi apakah itu benar-benar bermanfaat bagi keterbacaan dan pemeliharaan kode Anda untuk dapat menulis kode seperti:

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);
}

Pikirkan tentang potensi kesalahan di sini, apakah yang terakhir imencetak 0 atau 4? Sekarang ini adalah contoh yang sangat kecil, yang cukup mudah untuk diikuti dan dilacak, tetapi jelas jauh lebih mudah dirawat dan dibaca daripada menyatakan bagian luar idengan nama yang berbeda.

NB:

Harap perhatikan, aturan ruang lingkup C # berbeda dari aturan ruang lingkup C ++ . Dalam C + + variabel hanya dalam ruang lingkup dari mana mereka dideklarasikan sampai akhir blok. Yang akan membuat kode Anda konstruk yang valid di C ++.

Johannes Kommer
sumber
Masuk akal, saya pikir itu harus kesalahan pada ivariabel dalam ; yang tampaknya lebih jelas bagi saya.
George Duckett
Tetapi ruang lingkupnya adalah untuk perintah dan {}? Atau apakah itu berarti orangtuanya {}?
John V
2
Nah jika saya mendeklarasikan 'A' SETELAH pernyataan for, itu tidak valid dalam for loop seperti yang dinyatakan nanti. Itu sebabnya saya tidak mengerti mengapa nama yang sama tidak dapat digunakan.
John V
9
Mungkin jawaban ini harus, untuk kelengkapan, menunjukkan bahwa aturan C # lingkup berbeda dari yang untuk C ++ dalam kasus ini. Dalam C ++, variabel hanya dalam ruang lingkup dari tempat mereka dideklarasikan hingga akhir blok (lihat msdn.microsoft.com/en-us/library/b7kfh662(v=vs.80).aspx ).
AAT
2
Perhatikan bahwa Java mengambil pendekatan perantara antara C ++ dan C #: karena ini adalah contoh OP yang valid di Jawa, tetapi jika idefinisi luar dipindahkan sebelum for loop, idefinisi dalam akan ditandai sebagai tidak valid.
Nicola Musatti
29

Jawaban J.Kommer benar: secara singkat, adalah ilegal untuk variabel lokal dideklarasikan dalam ruang deklarasi variabel lokal yang tumpang tindih dengan ruang deklarasi variabel lokal lain yang memiliki lokal dengan nama yang sama.

Ada aturan tambahan C # yang dilanggar di sini juga. Aturan tambahan adalah bahwa ilegal untuk nama sederhana digunakan untuk merujuk pada dua entitas yang berbeda di dalam dua ruang deklarasi variabel lokal yang tumpang tindih. Jadi bukan saja contoh Anda ilegal, ini juga ilegal:

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

Karena sekarang nama sederhana "x" telah digunakan di dalam ruang deklarasi variabel lokal "y" berarti dua hal yang berbeda - "this.x" dan lokal "x".

Lihat http://blogs.msdn.com/b/ericlippert/archive/tags/simple+names/ untuk analisis lebih lanjut tentang masalah ini.

Eric Lippert
sumber
2
Juga, menarik untuk dicatat bahwa kode contoh Anda akan dikompilasi ketika Anda membuat perubahanint y = this.x;
Phil
4
@ Phil: Benar. this.xbukan nama yang sederhana .
Eric Lippert
13

Ada cara mendeklarasikan dan menggunakan idi dalam metode setelah loop:

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

    }

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

Anda dapat melakukan ini di Jawa (mungkin berasal dari C saya tidak yakin). Ini tentu saja agak berantakan demi nama variabel.

Chris S
sumber
Ya ini berasal dari C / C ++.
Branko Dimitrijevic
1
Saya tidak tahu ini sebelumnya. Fitur bahasa yang rapi!
@MathiasLykkegaardLorenzen ya
Chris S
7

Jika Anda sudah dinyatakan i sebelum Anda forlingkaran, menurut Anda masih harus valid untuk menyatakan itu di dalam loop?

Tidak, karena ruang lingkup keduanya akan tumpang tindih.

Sedangkan untuk tidak bisa melakukannya int A=i;, yah itu hanya karena ihanya ada di forloop, seperti yang seharusnya.

Widor
sumber
7

Selain jawaban J.Kommer (+1 btw). Ada ini dalam standar untuk lingkup NET:

blok Jika Anda mendeklarasikan variabel dalam konstruksi blok seperti pernyataan Jika, cakupan variabel itu hanya sampai akhir blok. Seumur hidup sampai prosedur berakhir.

Prosedur Jika Anda mendeklarasikan variabel dalam prosedur, tetapi di luar pernyataan Jika, ruang lingkupnya adalah sampai Sub Sub atau Fungsi Akhir. Masa pakai variabel adalah hingga prosedur berakhir.

Jadi int i decalared dalam for loop header akan berada dalam ruang lingkup hanya selama for loop block, TETAPI itu seumur hidup berlangsung sampai Main()kode selesai.

ChrisBD
sumber
5

Cara termudah untuk memikirkan hal ini adalah dengan memindahkan deklarasi luar I ke atas loop. Itu harus menjadi jelas kalau begitu.

Bagaimanapun juga cakupannya sama, oleh karena itu tidak dapat dilakukan.

Andrew Barber
sumber
4

Juga peraturan C # banyak waktu tidak diperlukan dalam hal memprogram secara ketat, tetapi apakah ada untuk menjaga kode Anda bersih dan mudah dibaca.

misalnya, mereka dapat membuatnya sehingga jika Anda mendefinisikannya setelah loop maka itu ok, namun seseorang yang membaca kode Anda dan melewatkan baris definisi mungkin berpikir itu ada hubungannya dengan variabel loop.

Orang
sumber
2

Jawaban Kommer secara teknis benar. Izinkan saya memprasafkannya dengan metafora layar-buta yang jelas.

Ada layar satu arah yang buta antara blok-untuk dan blok luar yang tertutup sehingga kode dari dalam-blok dapat melihat kode luar tetapi kode di blok luar tidak dapat melihat kode di dalamnya.

Karena kode luar tidak bisa melihat ke dalam, itu tidak bisa menggunakan apa pun yang dinyatakan di dalam. Tetapi karena kode di-blok dapat melihat di dalam dan di luar, variabel yang dideklarasikan di kedua tempat tidak dapat digunakan secara jelas dengan nama.

Jadi Anda tidak melihatnya, atau C #!

penjelajah
sumber
0

Lihatlah dengan cara yang sama seperti jika Anda dapat mendeklarasikan intdalam sebuah usingblok:

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

Namun, karena inttidak diterapkan IDisposable, ini tidak dapat dilakukan. Ini dapat membantu seseorang memvisualisasikan bagaimana suatu intvariabel ditempatkan dalam ruang lingkup pribadi.

Cara lain adalah dengan mengatakan,

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

Semoga ini bisa membantu memvisualisasikan apa yang sedang terjadi.

Saya sangat suka fitur ini, karena mendeklarasikan intdari dalam forloop menjaga kode tetap bagus dan kencang.

kode jp2
sumber
WTF? Jika Anda akan menandai NEG, nyatakanlah alasannya.
jp2code
Bukan downvoter dan saya pikir perbandingannya usingcukup ok, meskipun semantiknya agak berbeda (yang mungkin mengapa itu downvotated). Catat ituif (true) itu mubazir. Hapus dan Anda memiliki blok pelingkupan.
Abel