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?
Насколько я понимаю, первый пример должен подойти.
Вы прекрасно понимаете объем. Это не ошибка определения объема. Это непоследовательное использование простой ошибки имени.
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» для обозначения двух разных вещей в то же пространство объявления локальной переменной и соответственно сообщает об ошибке.
Мне кажется, что компилятор имеет в виду сказать, что i
был объявлен на уровне метода и ограничен рамками цикла for
.
Итак, в случае 1 - вы получаете сообщение об ошибке, что переменная уже существует, что она и делает
, а в случае 2 - поскольку переменная ограничена только в пределах цикла for
, она не может быть доступ вне этого цикла
Чтобы избежать этого, вы могли бы:
var i = 0;
for(i = 0, i < 10, i++){
}
i = 10;
но я не могу придумать случая, когда вы бы захотели это сделать.
HTH
вам необходимо выполнить
int i ;
for ( i = 0; i < 10; i++)
{
}
i = 10;
Из спецификации 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();
}
}
Да, я второй за комментарий "компилятор няни-государства". Что интересно, это нормально.
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;
}
}
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);
}
}
}
В первом примере объявление i вне цикла делает i локальной переменной функции. В результате будет ошибкой объявить другое имя переменной i в любом блоке этой функции.
Второй, i находится в области видимости только во время цикла. Вне цикла я больше не доступен.
Итак, вы видели ошибки, но в этом нет ничего плохого
for (int i = 0; i < 10; i++)
{
// do something
}
foreach (Foo foo in foos)
{
int i = 42;
// do something
}
Поскольку объем i ограничен в каждом блоке.
Или это просто случай компилятора состояния няни?
Именно так. Нет смысла «повторно использовать» имена переменных в одном методе. Это просто источник ошибок и не более того.
Это потому, что пространство объявлений определяет i
на уровне метода. Переменная i
находится вне области видимости в конце цикла, но вы по-прежнему не можете повторно объявить i
, потому что i
уже был определен в этом методе.
Область действия против пространства декларации:
Вы захотите взглянуть на ответ Эрика Липперта (который по умолчанию всегда прав в вопросах как это).
Вот комментарий Эрика к вышеупомянутому посту, который, я думаю, говорит о том, почему они сделали то, что они сделали:
Посмотрите на это с другой стороны. Всегда должно быть разрешено перемещать объявление переменной UP в исходном коде до тех пор, пока вы держите его в том же блоке, верно? Если бы мы сделали это так, как вы предложили, то это иногда было бы законным, а иногда и незаконным! Но чего мы действительно хотим избежать, так это того, что происходит в C ++ - в C ++ иногда перемещение объявления переменной вверх фактически изменяет {{1} }} привязки других простых имен!