переменный объем в блоках операторов

for (int i = 0; i < 10; i++)
{
    Foo();
}
int i = 10; // error, 'i' already exists

----------------------------------------    

for (int i = 0; i < 10; i++)
{
    Foo();
}
i = 10; // error, 'i' doesn't exist

Моим пониманием объема первый пример должен быть прекрасным. Факт, который не позволяют ни одному из них, кажется еще более нечетным. Конечно, 'я' или в объеме или нет.

Действительно ли там что-то неочевидно об объеме, который я не понимаю, какие средства компилятор действительно не может разрешить это? Или просто случай состояния няньки compilerism?

12
задан fearofawhackplanet 22 April 2010 в 17:43
поделиться

9 ответов

Насколько я понимаю, первый пример должен подойти.

Вы прекрасно понимаете объем. Это не ошибка определения объема. Это непоследовательное использование простой ошибки имени.

int i = 10; // ошибка, "i" уже существует

Это не та ошибка, о которой сообщается . Сообщается об ошибке: «Локальная переменная с именем i не может быть объявлена ​​в этой области , потому что она даст другое значение переменной i, которая уже используется в дочерней области для обозначения чего-то еще »

сообщение об ошибке сообщает вам, в чем ошибка; прочтите сообщение об ошибке еще раз. Нигде не говорится о конфликте между заявлениями; он говорит, что ошибка , потому что это меняет значение простого имени . Ошибка , а не повторное объявление; Совершенно законно иметь две вещи в двух разных областях с одним и тем же именем, даже если эти области вложены друг в друга. Что не законно, так это наличие одного простого имени, означающего две разные вещи во вложенных пространствах объявлений локальных переменных .

Вы получили бы ошибку «локальная переменная с именем i уже определена в этой области», если бы вместо этого вы сделали что-то вроде

int i = 10;
int i = 10;

Несомненно, «i» либо входит в область действия, либо нет.

Конечно, но что с того? Попадает ли данное i в область видимости или нет, не имеет значения. Например:

class C 
{
    int i;
    void M()
    {
        string i;

Совершенно законно. Внешний i находится в области видимости по всей M. Нет никаких проблем с объявлением локального i, которое затеняет внешнюю область видимости. Что было бы проблемой, если бы вы сказали

class C 
{
    int i;
    void M()
    {
        int x = i;
        foreach(char i in ...

, потому что теперь вы использовали i для обозначения двух разных вещей в двух вложенных пространствах объявления локальных переменных - переменной цикла и поля. Это сбивает с толку и подвержено ошибкам, поэтому мы делаем это незаконным.

Есть ли что-то неочевидное в области видимости, которое я не понимаю, что означает, что компилятор действительно не может решить эту проблему?

Я не понимаю вопроса. Очевидно, что компилятор способен полностью проанализировать программу; если компилятор не мог определить значение каждого использования i, то как он мог бы сообщать об ошибке? Компилятор может полностью определить, что вы использовали «i» для обозначения двух разных вещей в то же пространство объявления локальной переменной и соответственно сообщает об ошибке.

17
ответ дан 2 December 2019 в 04:08
поделиться

Мне кажется, что компилятор имеет в виду сказать, что i был объявлен на уровне метода и ограничен рамками цикла for .

Итак, в случае 1 - вы получаете сообщение об ошибке, что переменная уже существует, что она и делает

, а в случае 2 - поскольку переменная ограничена только в пределах цикла for , она не может быть доступ вне этого цикла

Чтобы избежать этого, вы могли бы:

var i = 0;

for(i = 0, i < 10, i++){
}

i = 10;

но я не могу придумать случая, когда вы бы захотели это сделать.

HTH

0
ответ дан 2 December 2019 в 04:08
поделиться

вам необходимо выполнить

            int i ;
            for ( i = 0; i < 10; i++)
            {

            }
            i = 10;
0
ответ дан 2 December 2019 в 04:08
поделиться

Из спецификации C # по объявлениям локальных переменных :

Объем объявленной локальной переменной {{1} } в объявлении-локальной-переменной - это блок , в котором происходит объявление.

Теперь, конечно, вы не можете использовать i до того, как он объявлен, но область действия объявления i - это весь блок , который содержит его:

{
    // scope starts here
    for (int i = 0; i < 10; i++)
    {
        Foo();
    }
    int i = 10;
}

Переменная for i находится в дочерней области, отсюда конфликт имен переменных.

Если мы изменим положение объявления, коллизия станет более ясной:

{
    int i = 10;

    // collision with i
    for (int i = 0; i < 10; i++)
    {
        Foo();
    }
}
5
ответ дан 2 December 2019 в 04:08
поделиться

Да, я второй за комментарий "компилятор няни-государства". Что интересно, это нормально.

for (int i = 0; i < 10; i++)
{

}

for (int i = 0; i < 10; i++)
{

}

и это нормально

for (int i = 0; i < 10; i++)
{

}

for (int j = 0; j < 10; j++)
{
    var i = 12;                
}

, но это не

for (int i = 0; i < 10; i++)
{
    var x = 2;
}

var x = 5;

, хотя вы можете это сделать

for (int i = 0; i < 10; i++)
{
    var k = 12;
}

for (int i = 0; i < 10; i++)
{
    var k = 13;
}

Все это немного непоследовательно.

РЕДАКТИРОВАТЬ

Основываясь на комментариях Эрика ниже, я подумал, что было бы полезно показать, как я пытаюсь справляться с циклами. По возможности я стараюсь составлять циклы в их собственный метод. Я делаю это, потому что это способствует удобочитаемости.

ДО

/*
 * doing two different things with the same name is unclear
 */
for (var index = 0; index < people.Count; index++)
{
    people[index].Email = null;
}
var index = GetIndexForSomethingElse(); 

ПОСЛЕ

/*
 * Now there is only one meaning for index in this scope
 */
ClearEmailAddressesFor(people); // the method name works like a comment now
var index = GetIndexForSomethingElse();

/*
 * Now index has a single meaning in the scope of this method.
 */
private void ClearEmailAddressesFor(IList<Person> people)
{
    for (var index = 0; index < people.Count; index++)
    {
        people[index].Email = null;
    }
}
2
ответ дан 2 December 2019 в 04:08
поделиться
class Test
{
    int i;
    static int si=9; 

    public Test()
    {
        i = 199;
    }

    static void main()
    {
        for (int i = 0; i < 10; i++)
        {
            var x = 2;
        }

        { var x = 3; }

        {    // remove outer "{ }" will generate compile error
            int si = 3; int i = 0;

             Console.WriteLine(si);
             Console.WriteLine(Test.si);
             Console.WriteLine(i);
             Console.WriteLine((new Test()).i);
        }
    }
}
0
ответ дан 2 December 2019 в 04:08
поделиться

В первом примере объявление i вне цикла делает i локальной переменной функции. В результате будет ошибкой объявить другое имя переменной i в любом блоке этой функции.

Второй, i находится в области видимости только во время цикла. Вне цикла я больше не доступен.

Итак, вы видели ошибки, но в этом нет ничего плохого

for (int i = 0; i < 10; i++)
{
  // do something
}

foreach (Foo foo in foos)
{
   int i = 42;
   // do something 
}

Поскольку объем i ограничен в каждом блоке.

1
ответ дан 2 December 2019 в 04:08
поделиться

Или это просто случай компилятора состояния няни?

Именно так. Нет смысла «повторно использовать» имена переменных в одном методе. Это просто источник ошибок и не более того.

0
ответ дан 2 December 2019 в 04:08
поделиться

Это потому, что пространство объявлений определяет i на уровне метода. Переменная i находится вне области видимости в конце цикла, но вы по-прежнему не можете повторно объявить i , потому что i уже был определен в этом методе.

Область действия против пространства декларации:

http://csharpfeeds.com/post/11730/Whats_The_Difference_Part_Two_Scope_vs_Declaration_Space_vs_Lifetime.aspx

Вы захотите взглянуть на ответ Эрика Липперта (который по умолчанию всегда прав в вопросах как это).

http://blogs.msdn.com/ericlippert/archive/2009/08/03/what-s-the-difference-part-two-scope-vs-declaration-space-vs-lifetime.aspx

Вот комментарий Эрика к вышеупомянутому посту, который, я думаю, говорит о том, почему они сделали то, что они сделали:

Посмотрите на это с другой стороны. Всегда должно быть разрешено перемещать объявление переменной UP в исходном коде до тех пор, пока вы держите его в том же блоке, верно? Если бы мы сделали это так, как вы предложили, то это иногда было бы законным, а иногда и незаконным! Но чего мы действительно хотим избежать, так это того, что происходит в C ++ - в C ++ иногда перемещение объявления переменной вверх фактически изменяет {{1} }} привязки других простых имен!

11
ответ дан 2 December 2019 в 04:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: