forループで宣言された変数はローカル変数ですか?


133

私はかなり長い間C#を使用してきましたが、次のことに気付いたことがありません。

 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
 }

では、この名前の変数を宣言できない場合、なぜforブロックの外で 'i'の値を使用できないのですか?

forループで使用されるイテレーター変数は、そのスコープ内でのみ有効であると思いました。


8
外側のブロックスコープにはforループのスコープが含まれているため
V4Vendetta

3
私がそれを考える1つの方法(および一部のコードスタイルガイドでは、特に動的型付き言語ではこれが必要です)は、スコープで宣言されたすべての変数をそのスコープの先頭で宣言できるため、関数を書き直すことができるということですint i, A; for(int i = 0; i < 5; i++){ } i=4; A=i
キース

2
@ V4Vendetta:それはもっと逆です。内部ブロックは、親ブロックへのブラックボックスです。
セバスチャンマッハ

4
これが不可能であるという技術的な理由は別として、なぜこれ(またはこれの変形)が賢明なことであるのでしょうか。それは明らかに異なる目的に役立つため、別の名前を付けます。
デイブ

私はこれにまったく気づきませんでしたが、完全にふさわしい制限のようです。
alan2here

回答:


119

forループ内とforループ外の両方で同じ名前の変数を定義できないのは、外側のスコープの変数が内側のスコープで有効であるためです。これが許可された場合、forループ内に2つの「i」変数が存在することを意味します。

参照:MSDNスコープ

具体的には:

local-variable-declaration(セクション8.5.1)で宣言されたローカル変数のスコープは、宣言が行われるブロックです。

そして

forステートメントのforイニシャライザ(セクション8.8.3)で宣言されたローカル変数のスコープは、forイニシャライザ、for条件、forイテレータ、およびforステートメントに含まれるステートメントです。

また、ローカル変数宣言(C#仕様のセクション8.5.1)

具体的には:

local-variable-declarationで宣言されたローカル変数のスコープは、宣言が行われるブロックです。ローカル変数のlocal-variable-declaratorの前にあるテキストの位置でローカル変数を参照すると、エラーになります。ローカル変数のスコープ内で、同じ名前の別のローカル変数または定数を宣言すると、コンパイル時エラーになります。

(エンファシス鉱山。)

つまり、iforループ内のスコープはforループです。iforループの外側のスコープは、メインメソッド全体 forループが。の2つの発生があることを意味しますi上記のように無効なループ内。

あなたが許可されていないint A = i;理由int iは、forループ内での使用に限定されているためです。したがって、forループの外側からはアクセスできなくなります。

ご覧のとおり、これらの問題は両方ともスコープの結果です。最初の問題(int i = 4;)はiforループスコープ内に2つの変数をもたらします。一方int A = i;、スコープ外の変数にアクセスすることになります。

代わりiに、メソッド全体をスコープとするように宣言し、それをメソッドとforループスコープの両方で使用できます。これにより、どちらのルールにも違反しなくなります。

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

編集

もちろん、C#コンパイラを変更して、このコードを非常に有効にコンパイルできるようにすることもできます。結局これは有効です:

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

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

しかし、次のようなコードを記述できることは、コードの可読性と保守性にとって本当に有益でしょうか。

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

ここで間違いの可能性について考えてください。最後のi出力は0または4ですか。これは非常に小さな例です。追跡と追跡が非常に簡単ですがi、別の名前でアウターを宣言するよりも、保守性と可読性がはるかに低くなります。

注意:

C#のスコープルールはC ++のスコープルールとは異なります。C ++では、変数は、宣言された場所からブロックの終わりまでの範囲内にのみ存在します。これにより、コードがC ++で有効な構成になります。


理にかなっていますが、内部i変数でエラーになるはずです。それは私にはもっと明白に思えます。
ジョージデュケット

しかし、スコープはコマンドとその{}のためのものですか?それとも親{}を意味しますか?
John V

2
まあ、forステートメントの後に 'A'を宣言すると、後で宣言されるため、forループでは無効になります。同じ名前を使用できないのはそのためです。
John V

9
おそらく、この回答は、完全を期すために、C#のスコープルールがこの場合のC ++のルールとは異なることを指摘しているはずです。C ++では、変数は、宣言された場所からブロックの終わりまでの範囲にのみ存在します(msdn.microsoft.com/en-us/library/b7kfh662(v=vs.80).aspxを参照)。
AAT

2
JavaはC ++とC#の中間的なアプローチを取ることに注意してください。OPの例はJavaでも有効ですが、外部i定義がforループの前に移動された場合、内部定義はi無効としてマークされます。
Nicola Musatti

29

J.Kommerの答えは正しいです。簡単に言うと、別のローカル変数宣言スペースと重複するローカル変数宣言スペースローカル変数を宣言することは違法です、同じ名前のローカルを持つ。

ここにも違反するC#の追加ルールがあります。追加のルールは、2つの異なるオーバーラップするローカル変数宣言スペース内の2つの異なるエンティティを参照するために単純な名前を使用することは違法であることです。だからあなたの例は違法であるだけでなく、これも違法です:

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

なぜなら、単純な名前「x」が「y」のローカル変数宣言スペース内で使用され、「this.x」とローカル「x」の2つの異なることを意味しているためです。

これらの問題の詳細な分析については、http://blogs.msdn.com/b/ericlippert/archive/tags/simple+names/を参照してください。


2
また、それはあなたが変更を行うときに、サンプルコードをコンパイルすることは興味深いint y = this.x;
フィル・

4
@フィル:正解。this.x単純な名前ではありません。
Eric Lippert、2011年

13

iループの後、メソッド内で宣言して使用する方法があります。

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

    }

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

Javaでこれを行うことができます(Cに由来する可能性がありますが、わかりません)。もちろん、変数名のために少し面倒です。


はい、これはC / C ++に由来します。
Branko Dimitrijevic

1
私は以前これを知りませんでした。きちんとした言語機能!

@MathiasLykkegaardLorenzenはい
Chris S

7

あなたが宣言したい場合はi 前に、あなたのforループ、あなたはまだループ内でそれを宣言するために有効であるべきだと思いますか?

いいえ、その場合、2つのスコープは重複します。

やることができないためとしてint A=i;、よくそれは単にためだiだけに存在しfor、それが何をすべきのように、ループ。


7

J.Kommerの回答に加えて(+1 btw)。これはNETスコープの標準にあります。

ブロック Ifステートメントなどのブロック構造内で変数を宣言する場合、その変数のスコープはブロックの終わりまでです。寿命は、手順が終了するまでです。

プロシージャプロシージャ 内で変数を宣言したが、Ifステートメントの外では、スコープはEnd SubまたはEnd Functionまでです。変数の存続期間は、プロシージャーが終了するまでです。

したがって、私はループのヘッダ内decalared INTは、ループブロックの間の範囲であろう、しかしそれの寿命は、まで続くMain()コード完了する。


5

これについて考える最も簡単な方法は、Iの外部宣言をループの上に移動することです。それが明らかになるはずです。

どちらの方法でも同じスコープなので、実行できません。


4

また、C#のルールは厳密なプログラミングの観点からは多くの場合不要ですが、コードをクリーンで読みやすく保つためにあります。

たとえば、ループの後に定義した場合は問題ないようにできますが、コードを読んで定義行を逃した人は、ループの変数に関係があると考えるかもしれません。


2

コマーの答えは技術的に正しいです。鮮やかなブラインドスクリーンのメタファーで言い換えてみましょう。

for-blockと外側のブロックの間に一方向のブラインドスクリーンがあり、for-block内からのコードは外側のコードを見ることができますが、外側のブロックのコードは内側のコードを見ることができません。

外部コードはinsideを認識できないため、内部で宣言されたものは使用できません。しかし、forブロック内のコードはinsideとoutsideの両方を認識できるため、両方の場所で宣言された変数を名前で明確に使用することはできません。

だからあなたはそれを見ないか、あなたはC#です!


0

ブロック内でを宣言できる場合と同じように見てください。intusing

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

ただし、intは実装していないためIDisposable、これを行うことはできません。intただし、変数がプライベートスコープにどのように配置されるかを視覚化するのに役立ちます。

別の言い方をすると、

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

これが起こっていることを視覚化するのに役立つことを願っています。

ループint内からfrom を宣言することでfor、コードを適切にタイトに保つことができるので、この機能が本当に好きです。


WTF?NEGにタグを付ける場合は、その理由を説明するボールを用意してください。
jp2code

反対投票でusingはなく、セマンティクスはかなり異なっていますが、比較は大丈夫だと思います(これがおそらく反対投票された理由です)。if (true)冗長であることに注意してください。それを削除すると、スコープブロックができます。
Abel
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.