Опубликован: 22.11.2005 | Уровень: специалист | Доступ: платный | ВУЗ: Тверской государственный университет
Лекция 5:

Переменные и выражения

< Лекция 4 || Лекция 5: 12 || Лекция 6 >

Локальные переменные

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

В некоторых языках, например в Паскале, локальные переменные должны быть объявлены в вершине процедурного блока. Иногда это правило заменяется менее жестким, но, по сути, аналогичным правилом, - где бы внутри процедурного блока ни была объявлена переменная, она считается объявленной в вершине блока, и ее область видимости распространяется на весь процедурный блок. В C#, также как и в языке C++, принята другая стратегия. Переменную можно объявлять в любой точке процедурного блока. Область ее видимости распространяется от точки объявления до конца процедурного блока.

На самом деле, ситуация с процедурным блоком в C# не так проста. Процедурный блок имеет сложную структуру; в него могут быть вложены другие блоки, связанные с операторами выбора, цикла и так далее. В каждом таком блоке, в свою очередь, допустимы вложения блоков. В каждом внутреннем блоке допустимы объявления переменных. Переменные, объявленные во внутренних блоках, локализованы именно в этих блоках, их область видимости и время жизни определяются этими блоками. Локальные переменные начинают существовать при достижении вычислений в блоке точки объявления и перестают существовать, когда процесс вычисления завершает выполнение операторов блока. Можно полагать, что для каждого такого блока выполняется так называемый пролог и эпилог. В прологе локальным переменным отводится память, в эпилоге память освобождается. Фактически ситуация сложнее, поскольку выделение памяти, а следовательно, и начало жизни переменной, объявленной в блоке, происходит не в момент входа в блок, а лишь тогда, когда достигается точка объявления локальной переменной.

Давайте обратимся к примеру. В класс Testing добавлен метод с именем ScopeVar, вызываемый в процедуре Main. Вот код этого метода:

/// <summary>
/// Анализ области видимости переменных
/// </summary>
/// <param name="x"></param>
public void ScopeVar(int x)
{
	int y =77; string s = name;
	if (s=="Точка1")
	{
			int u = 5; int v = u+y; x +=1;
			Console.WriteLine("y= {0}; u={1}; 
				v={2}; x={3}", y,u,v,x);
	}
	else
	{
			int u= 7; int v= u+y;
			Console.WriteLine("y= {0}; u={1}; v={2}", y,u,v);
	}
	//Console.WriteLine("y= {0}; u={1}; v={2}",y,u,v);
	//Локальные переменные не могут быть статическими.
	//static int Count = 1;
	//Ошибка: использование sum до объявления
	//Console.WriteLine("x= {0}; sum ={1}", x,sum);
	int i;long sum =0;
	for(i=1; i<x; i++)
	{
		//ошибка: коллизия имен: y
		//float y = 7.7f;
		sum +=i;
	}
	Console.WriteLine("x= {0}; sum ={1}", x,sum);
}//ScopeVar

Заметьте, в теле метода встречаются имена полей, аргументов и локальных переменных. Эти имена могут совпадать. Например, имя x имеет поле класса и формальный аргумент метода. Это допустимая ситуация. В языке C# разрешено иметь локальные переменные с именами, совпадающими с именами полей класса, - в нашем примере таким является имя y ; однако, запрещено иметь локальные переменные, имена которых совпадают с именами формальных аргументов. Этот запрет распространяется не только на внешний уровень процедурного блока, что вполне естественно, но и на все внутренние блоки.

В процедурный блок вложены два блока, порожденные оператором if. В каждом из них объявлены переменные с одинаковыми именами u и v. Это корректные объявления, поскольку время существования и области видимости этих переменных не пересекаются. Итак, для невложенных блоков разрешено объявление локальных переменных с одинаковыми именами. Заметьте также, что переменные u и v перестают существовать после выхода из блока, так что операторы печати, расположенные внутри блока, работают корректно, а оператор печати вне блока приводит к ошибке, - u и v здесь не видимы, кончилось время их жизни. По этой причине оператор закомментирован.

Выражение, проверяемое в операторе if, зависит от значения поля name. Значение поля глобально для метода и доступно всюду, если только не перекрывается именем аргумента (как в случае с полем x ) или локальной переменной (как в случае с полем y ).

Во многих языках программирования разрешено иметь локальные статические переменные, у которых область видимости определяется блоком, но время их жизни совпадает со временем жизни проекта. При каждом повторном входе в блок такие переменные восстанавливают значение, полученное при предыдущем выходе из блока. В языке C# статическими могут быть только поля, но не локальные переменные. Незаконная попытка объявления static переменной в процедуре ScopeVar закомментирована. Попытка использовать имя переменной в точке, предшествующей ее объявлению, также незаконна и закомментирована.

Глобальные переменные уровня процедуры. Существуют ли?

Поскольку процедурный блок имеет сложную структуру с вложенными внутренними блоками, то и здесь возникает тема глобальных переменных. Переменная, объявленная во внешнем блоке, рассматривается как глобальная по отношению к внутренним блокам. Во всех известных мне языках программирования во внутренних блоках разрешается объявлять переменные с именем, совпадающим с именем глобальной переменной. Конфликт имен снимается за счет того, что локальное внутреннее определение сильнее внешнего. Поэтому область видимости внешней глобальной переменной сужается и не распространяется на те внутренние блоки, где объявлена переменная с подобным именем. Внутри блока действует локальное объявление этого блока, при выходе восстанавливается область действия внешнего имени. В языке C# этот гордиев узел конфликтующих имен разрублен, - во внутренних блоках запрещено использование имен, совпадающих с именем, использованным во внешнем блоке. В нашем примере незаконная попытка объявить во внутреннем блоке уже объявленное имя y закомментирована.

Обратите внимание, что подобные решения, принятые создателями языка C#, не только упрощают жизнь разработчикам транслятора. Они способствуют повышению эффективности программ, а самое главное, повышают надежность программирования на C#.

Отвечая на вопрос, вынесенный в заголовок, следует сказать, что глобальные переменные на уровне процедуры в языке C#, конечно же, есть, но нет конфликта имен между глобальными и локальными переменными на этом уровне. Область видимости глобальных переменных процедурного блока распространяется на весь блок, в котором они объявлены, начиная от точки объявления, и не зависит от существования внутренних блоков. Когда говорят, что в C# нет глобальных переменных, то, прежде всего, имеют в виду их отсутствие на уровне модуля. Уже во вторую очередь речь идет об отсутствии конфликтов имен на процедурном уровне.

Константы

Константы C# могут появляться, как обычно, в виде литералов и именованных констант. Вот пример константы, заданной литералом и стоящей в правой части оператора присваивания:

const float y = 7.7f;

Значение константы " 7.7f " является одновременно ее именем, оно же позволяет однозначно определить тип константы. Заметьте, иногда, как в данном случае, приходится добавлять к значению специальные символы для точного указания типа. Я не буду останавливаться на этих подробностях. Если возникает необходимость уточнить, как записываются литералы, то достаточно получить справку по этой теме. Делается все так же, как и в языке C++.

Всюду, где можно объявить переменную, можно объявить и именованную константу. Синтаксис объявления схож. В объявление добавляется модификатор const, инициализация констант обязательна и не может быть отложена. Инициализирующее выражение может быть сложным, важно, чтобы оно было вычислимым в момент его определения. Вот пример объявления констант:

/// <summary>
/// Константы
/// </summary>
public void Constants()
{
	const int SmallSize = 38, LargeSize =58;
	const int MidSize = (SmallSize + LargeSize)/2;
	const double pi = 3.141593;
	//LargeSize = 60; //Значение константы нельзя изменить.
	Console.WriteLine("MidSize= {0}; pi={1}",
		MidSize, pi);
}//Constants
< Лекция 4 || Лекция 5: 12 || Лекция 6 >
Александр Галабудник
Александр Галабудник

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

Александра Гусева
Александра Гусева
Сергей Кузнецов
Сергей Кузнецов
Россия, Москва
Pavel Kuchugov
Pavel Kuchugov
Россия, Московский инженерно-физический институт, 2010