Переменная область в блоках операторов
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
По моему пониманию сферы применения, первый пример должен быть точным. Тот факт, что ни один из них не разрешен, кажется еще более странным. Разумеется, "i" - либо в сфере охвата, либо нет.
Есть ли что-то неочевидное в области видимости, которую я не понимаю, что означает, что компилятор действительно не может это решить? Или это всего лишь случай использования компилятора в nanny-state?
Ответы
Ответ 1
По моему пониманию сферы применения, первый пример должен быть точным.
Ваше понимание сферы в порядке. Это не ошибка определения области видимости. Это непоследовательное использование простой ошибки имени.
int я = 10;//ошибка, 'i' уже существует
Это не ошибка, о которой сообщается. Ошибка, о которой сообщается, - "локальная переменная с именем я не может быть объявлена в этой области, потому что она придавала бы другому смысл i, который уже используется в дочерней области для обозначения чего-то еще"
Сообщение об ошибке сообщает вам, что такое ошибка; снова прочитайте сообщение об ошибке. Нигде не говорится, что существует конфликт между декларациями; он говорит, что ошибка связана с тем, что это меняет смысл простого имени. Ошибка: не повторная декларация; вполне законно иметь две вещи в двух разных областях, имеющих одно и то же имя, даже если эти области гнездятся. То, что не является законным, состоит в том, чтобы одно простое имя означало две разные вещи во вложенных локальных пространствах переменных переменных.
Вы получите ошибку "локальная переменная с именем я уже определена в этой области", если вместо этого вы сделали что-то вроде
int i = 10;
int i = 10;
Разумеется, "i" находится либо в области видимости, либо нет.
Конечно, но так что? Независимо от того, является ли данный я областью действия или нет, это не имеет значения. Например:
class C
{
int i;
void M()
{
string i;
Совершенно законный. Внешний я находится в области видимости по всему M. Нет никаких проблем с объявлением локального i, который затеняет внешнюю область. Что было бы проблемой, если бы вы сказали
class C
{
int i;
void M()
{
int x = i;
foreach(char i in ...
Потому что теперь вы использовали я для обозначения двух разных вещей в двух вложенных локальных объявлениях переменных переменных - переменной цикла и поле. Это запутанное и подверженное ошибкам, поэтому мы делаем его незаконным.
Есть ли что-то неочевидное в области видимости, которую я не понимаю, что означает, что компилятор действительно не может это решить?
Я не понимаю вопроса. Очевидно, что компилятор способен полностью анализировать программу; Если компилятор не смог решить смысл каждого использования i, то как он мог сообщить об ошибке? Компилятор полностью может определить, что вы использовали "i" для обозначения двух разных вещей в одно и то же локальное пространство объявления переменных и соответственно сообщает об ошибке.
Ответ 2
Это потому, что пространство декларации определяет i
на уровне метода. Переменная i
выходит за пределы области действия в конце цикла, но вы все еще не можете обновить i
, потому что i
уже был определен в этом методе.
Scope vs Application Space:
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
Вот комментарий от eric на вышеупомянутом посту, который, я думаю, рассказывает о том, почему они сделали то, что они сделали:
Посмотрите на это так. Он всегда должен быть законным для переноса декларации переменная UP в исходном коде так долго как вы держите его в одном блоке, правильно? Если бы мы сделали это так, как вы предположим, тогда это иногда юридические, а иногда и незаконные! Но то, чего мы действительно хотим избежать, что происходит в С++ - на С++, иногда перемещение переменной декларация фактически меняет привязки других простых имен!
Ответ 3
Из спецификация С# в объявлениях локальных переменных:
Объем объявленной локальной переменной в декларации с локальной переменной блок, в котором происходит объявление.
Теперь, конечно, вы не можете использовать 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();
}
}
Ответ 4
В первом примере объявление я за пределами цикла делает я локальной переменной функции. В результате это ошибка, когда в любом блоке этой функции объявляется другое имя переменной i.
Во-вторых, я находится в области видимости только во время цикла. Вне цикла я больше не могу получить доступ.
Итак, вы видели ошибки, но нет ничего плохого в этом.
for (int i = 0; i < 10; i++)
{
// do something
}
foreach (Foo foo in foos)
{
int i = 42;
// do something
}
Поскольку область я ограничена в каждом блоке.
Ответ 5
Да, я второй комментарий компилятора "nanny-state". Интересно, что это нормально.
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;
}
}
Ответ 6
Или просто случай nanny-state compilerism?
Именно это. Нет смысла в "повторном использовании" имен переменных в том же методе. Это просто источник ошибок и ничего более.
Ответ 7
Мне кажется, что компилятор означает, что i
был объявлен на уровне метода и помечен в цикле for
.
Итак, в случае 1 - вы получаете ошибку, что переменная уже существует, что она делает
& в случае 2 - поскольку переменная ограничена только в цикле for
, к ней нельзя получить доступ за пределами этого цикла
Чтобы этого избежать, вы можете:
var i = 0;
for(i = 0, i < 10, i++){
}
i = 10;
но я не могу придумать случай, когда вы захотите сделать это.
НТН
Ответ 8
вам нужно сделать
int i ;
for ( i = 0; i < 10; i++)
{
}
i = 10;
Ответ 9
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);
}
}
}