Файл: Типы данных, пространство имен, классификация типов среды .NET.pdf
Добавлен: 23.04.2023
Просмотров: 104
Скачиваний: 3
СОДЕРЖАНИЕ
eStatus, boxVolume, arraySize, intI.[2]
Типы данных, пространство имен, классификация типов среды .NET
Объявление переменных, синтаксис элементарных типов, безопасность типов
Арифметические операции. Приоритеты операций. Логические операции
Размерные и ссылочные типы, особенности использования стека и кучи
аналогичному логическому типу данных и 0, и 1, и -1, то С# уже такого не допускает:
// Вольница с типом bool закончилась! [18]
bool b = 0; // Так в С# уже нельзя.
bool b2 = -1; // Так тоже нельзя.
bool b3 = true; // А вот так - можно.
bool b4 = false; // И так можно.
Еще одна важная вещь, о которой нельзя не упомянуть, - то, что для текстовых данных в С# теперь используются только два типа данных: string и char. Думаю, что многие программисты почувствуют невыразимое облегчение, узнав, что отпала потребность в таких замечательных типах данных, как char*, wchar_t*, LPSTR, LPCSTR, BSTR и OLECHAR. Многие программисты согласятся со мной, что операции с текстовыми данными в СОМ и Win32 производились не самым простым способом. В этом отношении .NET, где для работы с текстом предназначены только два типа данных (оба работают с Unicode), - это большой шаг вперед. [19]
Некоторые типы данных применяют так широко, что для работы с ними во многих компиляторах предусмотрен упрощенный синтаксис. Например, целую переменную можно создать так:
System.Int32 = new System.Int32();
Конечно, подобный синтаксис для объявления и инициализации целой переменной кажется громоздким. К счастью, многие компиляторы (включая С#) позволяют использовать и более простые выражения, например:
int а = 0;
Такой код читается намного лучше, да и компилятор в обоих случаях генерирует идентичный IL-код для System.Int32. Типы данных, которые поддерживаются компилятором напрямую, называются элементарными типами (primitive types) и отображаются им в типы из библиотеки классов NET Framework Class Library (PCL). Так, С#-типу int соответствует System.Int32. Значит, весь следующий код компилируется без ошибок и преобразуется в одинаковые команды IL: [20]
int a = 0; // Самый удобный синтаксис.
System.Int32 а = 0; // Удобный синтаксис.
int a = new int(); // Неудобный синтаксис.
System.Int32 а = new System.In32 (); // Самый неудобный синтаксис. [21]
Несколько лет назад запущенная в космос ракета разбилась через несколько минут после запуска, что стоило NАSA миллионов долларов. В результате расследования было обнаружено, что катастрофа произошла по вине программного обеспечения. В частности, ошибка была найдена в строке кода на С, и которой программист ошибочно использовал один знак равно (=) вместо требовавшихся двух (==), т.е. оператор присваивания, а не проверку на равенство. Из-за того, что С не обеспечивает безопасности типов, программа для ракеты неявно преобразовала результат присваивания в логическое значение true.
Если вы хоть немного программировали на С/С++, то наверняка совершали похожие ошибки. Такие ошибки трудно обнаружить; можно провести часы, просматривая исходный код и поисках неявного приведения типа, вызывающего проблему. Трата времени на поиски подобных ошибок неприемлема в корпоративных средах.
С# является строго типизированным языком. Помимо всего прочего, это означает, что тип Boolean не будет автоматически преобразован в целый тип. Для выполнения такого преобразования необходимо использовать явное приведение типа.
Более того, С# позволяет задавать, как определенные пользователем типы будут вести себя в контексте неявных и явных преобразований типов. [22]
Выводом данной главы может послужить то, что переменным в обязательном случае нужно задавать тип, инициализировать до момента первого использования, их можно и нужно записывать удобным синтаксисом. А также стоит уделить внимание правильному написанию операторов.
Область действия переменных
Выше были рассмотрены базовые типы переменных, для которых определены имя, тип, размер, значение. Но для каждой переменной существует еще и ограничение, задающее ту часть программы, где на нее можно ссылаться.
Основное правило: переменная, объявленная внутри блока, ограниченного фигурными скобками, доступна в программе от места ее объявления до конца блока, то есть до точки перед закрывающей блок скобкой.
После выхода из блока переменная недоступна.
Если переменная объявлена, но не используется, компилятор выдаст предупреждение. Если алгоритм допускает вариант, когда переменной может быть либо присвоено значение, либо не присвоено никакого значения, компилятор укажет на ошибку.
Переменная должна быть определена до первого ее использования.
Если переменная объявлена в каком-то блоке и в этот блок после объявления переменной входит другой блок, то оговоренная переменная доступна и во вложенном блоке.
Необходимо также соблюдать следующее правило: ни одна переменная, объявленная во вложенном блоке, не должна иметь имя, совпадающее с именем переменной во внешнем блоке, компилятор укажет на ошибку.
В листинге 2 приведена программа, систематизирующая правила доступа к переменной. Результат представлен на рис. 3.
Переменные i и j объявлены в блоке Main и доступны во всей программе. Используем i как счетчик итераций внешнего блока-тела for, а j — как суммарный счетчик итераций вложенного блока for. Вывод их окончательных значений расположен в конце программы, за границами внешнего блока for. Переменная m объявлена во внешнем блоке for и поэтому может быть использована как счетчик итераций вложенного блока for. Используется во вложенном блоке, а выведена во внешнем (в том же, где объявлена). [23]
После выхода из внешнего блока for, то есть уже в блоке Main, снова объявить переменную с именем m нельзя, так как идентификатор уже занят во внешнем блоке for, внутреннем по отношению к блоку Main.
Переменные с одинаковыми именами могут объявляться в последовательных блоках, то есть тех, которые не являются друг для друга ни внешними, ни внутренними. Такие два блока с переменой k (тела двух последовательно записанных циклов for) завершают программу в листинге 2.
Листинг 2 - Доступ к переменным
using System;
namespace ConsoleApp7
{
class Program
{
static void Main(string[] args)
{
int i = 0, j = i; // для всей программы – блока Main
for (; (i + j) < 11;)
{//внешний блок
++i; // счетчик внешнего блока
int m = 0; //переменная для внешнего и вложенного блоков
for (; (m + j) < 17;)
{//вложенный блок
m++; //счетчик одного вложенного блока
j++; //общий счетчик вложенных блоков
}// конец вложенного блока[24]
Console.WriteLine(" внутренний блок: m ="+m);
}//конец внешнего блока
Console.WriteLine("\n\n внешний блок: i ="+i);
Console.WriteLine(" все вложенные блоки: j ="+j);
Console.WriteLine("\n последовательные блоки");
i = 0; // счетчик
for (int k = 0; k < 21; k++) i++;
Console.WriteLine(" i ="+i);
i = 0; // счетчик
for (int k = 0; k < 321; k++) i++;
Console.WriteLine(" i ="+i +"\n");
}
}
}
Рисунок 3 - Результат исполнения программы в листинге 2[25]
В приведенной выше программе можно уяснить для себя логику при работе с оператором цикла for, который служит для многократного выполнения блока до тех пор, пока выполняется условие. Также можно понять на примере где в какой области действуют переменные.
Арифметические операции. Приоритеты операций. Логические операции
Арифметические операции сложения, вычитания, умножения и деления применимы к любым числовым типам. При делении целых чисел остаток будет отброшен.
В многочленных арифметических операциях поддерживается тот же приоритет выполнения операций, что и в обычных алгебраических, то есть сначала выполняются умножение, деление и вычисление остатка, потом - сложение и вычитание. Например, в выражении
i = k+d*i/m%n-1;
действия выполняются в следующем порядке: умножение, деление, вычисление остатка, сложение, вычитание.
Для выполнения действий с нарушением естественного приоритета используются круглые скобки по обычным алгебраическим правилам.
В С# имеются сокращенные (и предпочтительные) формы записи арифметических операций.
Примеры сокращенных записей:
Операции присваивания:
с+=3; равнозначно с=с+3;
с-=3; равнозначно с=с-3;
с*=3; равнозначно с=с*3;
с/=3; равнозначно с=с/З;
с%=3; равнозначно с=с%3;
Некоторые авторы считают, что +=, -=, *=, /=и %= также являются опера- торами. С этим трудно согласиться, так как здесь предполагаются сочетания двух операций.
Операция инкремента:
i++; постфиксная форма, равнозначно i=i+1; или i+=1;
++i; префиксная форма, равнозначно i=i+1; или i+=1; [26]
Операция декремента:
i--; постфиксная форма, равнозначно i=i-1; или i-=1;
--i; префиксная форма, равнозначно i=i-1; или i-=1;
Операции инкремента и декремента применяются к целым числам и к числам с плавающей точкой. Различия между постфиксной и префиксной формами иллюстрируются приведенным ниже примером.
k=a+(i++)+d*(--j);
равнозначно
--j; k=a+i+d*j; i++;
Заметим, что в приведенном примере круглые скобки не изменяют приоритеты, а отделяют друг от друга операторы * и -- во избежание их превратного толкования компилятором или программистом, хотя можно было бы ограничиться вставкой пробела. Разумеется, такие выражения, как, например:
++i; и i++;
равнозначны.
Перейдем теперь к вопросу рассмотрения логических операций. Операции отношения (сравнения) и логические предполагают проверку некоторого условия с последующим действием, выполнение которого зависит от результата проверки. Приведем пример простейшего выражения такого типа с использованием ключевого слова if (если):
If (a<b) a+=b;
Если условие a<b выполняется, то есть истинно (true), производится операция сложения a+=b; . Если условие a<b не выполняется, то есть ложно (false), операция сложения не производится (игнорируется).
В С# на равенство (= =) и неравенство (!=) можно проверять все переменные, но операторы сравнения (< и >) можно применять только к переменным числовых типов и типа char. В последнем случае сравниваются коды символов. [27]
Решение может приниматься в результате анализа не обязательно одного условия. Условия связываются знаками логического сложения && (И) или логического умножения || (ИЛИ).
Например:
if (i= = j && k!=5) a=1.2;
if (i= =j || k!=5) a=1.2;
В первом случае для присваивания переменной а значения 1.2 необходимо выполнение обоих условий, во втором — достаточно соблюдения одного из условий. Второй операнд условия вычисляется только при необходимости. Например: если не выполняется условие i= = j, то в первом выражении второе условие k! =5 не проверяется, если условие i= = j выполняется, то во втором выражении условие k! =5 не проверяется.
При выполнении таких операций производится только сравнение, значения участвующих в них операндов не изменяются. Механизм тот же, что и при приведении типов.
Примеры операций сравнения и логических показаны в листинге 3. Результат исполнения программы см. на рисунке 4.
В первом примере производится сравнение значений целых переменных i и j. При выполнении условия i>j, то есть true, выводится соответствующая надпись.
Далее сравниваются переменные типа double. Если условие d1>d2 не выполняется, то есть false, то обусловленное им действие игнорируется.
Третий абзац демонстрирует сочетания двух условий, связанных операторами || и &&.
В последнем абзаце используются непосредственно булевы переменные (b1 и b2).
Листинг 3 - Логические операции
using System;
namespace ConsoleApp4[28]
{
class Program
{
static void Main(string[] args)
{
int i = 55, j = -12;
if (i >j) Console.WriteLine(" i>j");//выполняется
double d1 = 0.34, d2 = 99.2, d = 0.6e-15;
if (d1 > d2) Console.WriteLine(" d1>d2");//не выполняется
if (d1 < d2) Console.WriteLine("d1<d2");//выполняется
if (d1 - d2 > d || d1 - d2 < d) Console.WriteLine("||");/*выполняется втрое условие*/
if (i > j && j <= 1) Console.WriteLine(" &&");//выполн.
if (i == 55 && j <= 1) Console.WriteLine(" !! &&"); //выполн.
bool b1 = true, b2 = false;
if (b1) Console.WriteLine(" " + b1);//выполняется
if (b2) Console.WriteLine(" " + b2);//не выполняется
if (!b2) Console.WriteLine(" "+!b2);//выполняется, !b2=true
if (b1!= b2) Console.WriteLine(" b1!=b2");//выполняется
if (true) Console.WriteLine(" *true* ");//выполняется
if (false) Console.WriteLine(" *false* ");//не выполняется
if (!false) Console.WriteLine(" *!false* ");//выполняется, !false=true
if (b1 != b2) Console.WriteLine(" b1 != b2");//выполняется
}//конец Main
}//конец Program
}[29]
Рисунок 4 - Результат исполнения программы из листинга 3[30]
Резюмируя написанное важно отметить, что в перспективе удобно применять сокращенные варианты арифметических операций, а также логические операторы && (И) и || (ИЛИ) при работе с переменными.