Страница 16 из 188
И последнее замечание: одновременно можно объявить только одну неявно типизированную переменную. Поэтому объявлениеvar s1 = 4.0, s2 = 5.0; // Ошибка!
является неверным и не может быть скомпилировано. Ведь в нем предпринимаетсяпопытка объявить обе переменные, s1 и s2, одновременно.Область действия и время существования переменных
Все переменные, использовавшиеся в предыдущих примерах программ, объявлялись в самом начале метода Main(). Но в C# локальную переменную разрешаетсяобъявлять в любом кодовом блоке. Как пояснялось в главе 2, кодовый блок начинается открывающей фигурной скобкой и оканчивается закрывающей фигурной скобкой.Этот блок и определяет область действия. Следовательно, всякий раз, когда начинаетсяблок, образуется новая область действия. Прежде всего область действия определяетвидимость имен отдельных элементов, в том числе и переменных, в других частях программы без дополнительного уточнения. Она определяет также время существованиялокальных переменных.
В C# к числу наиболее важных относятся области действия, определяемые классоми методом. Рассмотрение области действия класса (и объявляемых в ней переменных)придется отложить до того момента, когда в этой книге будут описываться классы.А до тех пор будут рассматриваться только те области действия, которые определяются методом иди же в самом методе.
Область действия, определяемая методом, начинается открывающей фигурнойскобкой и оканчивается закрывающей фигурной скобкой. Но если у этого метода имеются параметры, то и они входят в область действия, определяемую данным методом.
Как правило, локальные переменные объявляются в области действия, невидимойдля кода, находящегося вне этой области. Поэтому, объявляя переменную в определенной области действия, вы тем самым защищаете ее от доступа иди видоизменениявне данной области. Разумеется, правила области действия служат основанием для инкапсуляции.
Области действия могут быть вложенными. Например, всякий раз, когда создаетсякодовый блок, одновременно образуется и новая, вложенная область действия. В этомслучае внешняя область действия охватывает внутреннюю область. Это означает, чтолокальные переменные, объявленные во внешней области действия, будут видимы длякода во внутренней области действия. Но обратное не справедливо: локальные переменные, объявленные во внутренней области действия, не будут видимы вне этой области.
Для того чтобы стала более понятной сущность вложенных областей действия, рассмотрим следующий пример программы.// Продемонстрировать область действия кодового блока.using System;class ScopeDemo { static void Main() { int x; // Эта переменная доступна для всего кода внутри метода Main(). х = 10; if(x == 10) { // начать новую область действия int у = 20; // Эта переменная доступна только в данном кодовом блоке. // Здесь доступны обе переменные, х и у. Console.WriteLine("х и у: " + х + " " + у); х = у * 2; } // у = 100; // Ошибка! Переменна у здесь недоступна. // А переменная х здесь по-прежнему доступна. Console.WriteLine("х равно " + х); }}
Как поясняется в комментариях к приведенной выше программе, переменная хобъявляется в начале области действия метода Main(), и поэтому она доступна длявсего последующего кода в пределах этого метода. В блоке условного оператора ifобъявляется переменная у. А поскольку этот кодовый блок определяет свою собственную область действия, то переменная у видима только для кода в пределах данногоблока. Именно поэтому строка line у = 100;, находящаяся за пределами этогоблока, закомментирована. Если удалить находящиеся перед ней символы комментария (//), то во время компиляции программы произойдет ошибка, поскольку переменная у невидима за пределами своего кодового блока. В то же время переменная хможет использоваться в блоке условного оператора if, поскольку коду из этого блока,находящемуся во вложенной области действия, доступны переменные, объявленныев охватывающей его внешней области действия.
Переменные могут быть объявлены в любом месте кодового блока, но они становятся действительными только после своего объявления. Так, если объявить переменнуюв начале метода, то она будет доступна для всего остального кода в пределах этогометода. А если объявить переменную в конце блока, то она окажется, по существу, бесполезной, поскольку не будет доступной ни одному коду.
Если в объявление переменной включается инициализатор, то такая переменнаяинициализируется повторно при каждом входе в тот блок, в котором она объявлена.Рассмотрим следующий пример программы.// Продемонстрировать время существования переменной.using System;class VarInitDemo { static void Main() { int x; for(x = 0; x < 3; x++) { int у = -1; // Переменная у инициализируется при каждом входе в блок. Console.WriteLine("у равно: " + у); // Здесь всегда выводится -1 у = 100; Console.WriteLine("у теперь равно: " + у); } }}
Ниже приведен результат выполнения этой программы.у равно: -1у теперь равно: 100у равно: -1у теперь равно: 100у равно: -1у теперь равно: 100
Как видите, переменная у повторно инициализируется одним и тем же значением-1 при каждом входе во внутренний цикл for. И несмотря на то, что после этого цикла ей присваивается значение 100, оно теряется при повторной ее инициализации.
В языке C# имеется еще одна особенность соблюдения правил области действия: несмотря на то, что блоки могут быть вложены, ни у одной из переменных из внутреннейобласти действия не должно быть такое же имя, как и у переменной из внешней области действия. В приведенном ниже примере программы предпринимается попыткаобъявить две разные переменные с одним и тем же именем, и поэтому программа неможет быть скомпилирована./* В этой программе предпринимается попытка объявить во внутренней области действия переменную с таким же самым именем, как и у переменной, определенной во внешней области действия.*** Эта программа не может быть скомпилирована. ****/using System;class NestVar { static void Main() { int count; for (count = 0; count < 10; count = count+1) { Console.WriteLine("Это подсчет: " + count); int count; // Недопустимо!!! for(count = 0; count < 2; count++) Console.WriteLine("В этой программе есть ошибка!"); } }}Если у вас имеется некоторый опыт программирования на С или C++, то вам должно быть известно, что на присваивание имен переменным, объявляемым во внутреннейобласти действия, в этих языках не существует никаких ограничений. Следовательно,в С и C++ объявление переменной count в кодовом блоке, входящем во внешний циклfor, как в приведенном выше примере, считается вполне допустимым. Но в С и C++ такое объявление одновременно означает сокрытие внешней переменной. РазработчикиC# посчитали, что такого рода сокрытие имен может легко привести к программнымошибкам, и поэтому решили запретить его.## Преобразование и приведение типовВ программировании нередко значения переменных одного типа присваиваютсяпеременным другого типа. Например, в приведенном ниже фрагменте кода целое значение типа int присваивается переменной с плавающей точкой типа float.
int i;float f;i = 10;f = i; // присвоить целое значение переменной типа floatЕсли в одной операции присваивания смешиваются совместимые типы данных,то значение в правой части оператора присваивания автоматически преобразуетсяв тип, указанный в левой его части. Поэтому в приведенном выше фрагменте кодазначение переменной i сначала преобразуется в тип float, а затем присваиваетсяпеременной f. Но вследствие строгого контроля типов далеко не все типы данныхв С# оказываются полностью совместимыми, а следовательно, не все преобразования типов разрешены в неявном виде. Например, типы bool и int несовместимы.Правда, преобразование несовместимых типов все-таки может быть осуществленопутем приведения. Приведение типов, по существу, означает явное их преобразование. В этом разделе рассматривается как автоматическое преобразование, так и приведение типов.### Автоматическое преобразование типовКогда данные одного типа присваиваются переменной другого типа, неявное преобразование типов происходит автоматически при следующих условиях:* оба типа совместимы;* диапазон представления чисел целевого типа шире, чем у исходного типа.*Если оба эти условия удовлетворяются, то происходит расширяющее преобразование.Например, тип int достаточно крупный, чтобы вмещать в себя все действительныезначения типа byte, а кроме того, оба типа, int и byte, являются совместимыми целочисленными типами, и поэтому для них вполне возможно неявное преобразование.Числовые типы, как целочисленные, так и с плавающей точкой, вполне совместимыдруг с другом для выполнения расширяющих преобразований. Так, приведенная нижепрограмма составлена совершенно правильно, поскольку преобразование типа longв тип double является расширяющим и выполняется автоматически.