Файл: Типы данных, пространство имен, классификация типов среды .NET.pdf
Добавлен: 23.04.2023
Просмотров: 109
Скачиваний: 3
СОДЕРЖАНИЕ
eStatus, boxVolume, arraySize, intI.[2]
Типы данных, пространство имен, классификация типов среды .NET
Объявление переменных, синтаксис элементарных типов, безопасность типов
Арифметические операции. Приоритеты операций. Логические операции
Размерные и ссылочные типы, особенности использования стека и кучи
Размерные и ссылочные типы, особенности использования стека и кучи
Подобно языкам C++ и Java, C# подразделяет типы на два вида: встроенные типы, которые определены в языке, и определяемые пользователем типы, которые выбирает программист. С# также подразделяет типы на две другие категории: размерные и ссылочные.[31]
Размерные типы (value type) – это сравнительно небольшие по объему данные, которые представимы в виде последовательности битов. Переменные ссылочного типа (reference type) указывают на местоположение той или иной последовательности битов. [32]
Основное различие между ними - это способ, которым их значения сохраняются в памяти. Размерные типы сохраняют свое фактическое значение в стеке. Ссылочные типы хранят в стеке лишь адрес объекта, а сам объект сохраняется в куче. Куча — основная память программ, доступ к которой осуществляется на много медленнее чем к стеку. Если вы работаете с очень большими объектами, то сохранение их в куче имеет много преимуществ. С# также поддерживает и указатели на типы, но они редко употребляются. Применение указателей связано с использованием неуправляемого кода. [33]
One of the biggest advantages of the .NET Framework is the built-in automatic memory management. It protects the programmers from the complex task of manually allocating memory for objects and then waiting for a suitable moment to release it. This significantly increases the developer productivity and the quality of the programs written in C#. In the .NET Framework, there is a special component of the CLR that looks after memory management. It is called a "garbage collector" (automated memory cleaning system). The garbage collector has the following main tasks: to check when the allocated memory for variables is no longer in use, to release it and make it available for allocation of new objects. [34]
Стек - это структура данных, которая сохраняет элементы по принципу: первым пришел, последним ушел (полная противоположность очереди). Стек относится к области памяти, поддерживаемой процессором, в которой сохраняются локальные переменные. Доступ к стеку во много раз быстрее, чем к общей области памяти, поэтому использование стека для хранения данных ускоряет работу вашей программы. [35]
Стек напоминает стопку тарелок: если нужна чистая тарелка, берут тарелку из стопки (то есть ту, которую положили последней). С переменными
программы происходит то же самое: при вызове функции все объявленные в ней переменные заталкиваются в стек (эта операция называется push); если эта функция вызовет другую функцию, то все переменные, объявленные новой функцией, тоже попадут в стек. Когда функция, вызванная последней, завершится, ее переменные выходят из области видимости (scope) программы и выталкиваются из стека (эта операция называется pop); в результате память, занимаемая этими переменными, освобождается, и выполнение программы продолжается. [36]
В С# размерные типы (например, целые числа) располагаются в стеке: для их значений зарезервирована область в стеке, и доступ к ней осуществляется по названию переменной.
Ссылочные типы (например, объекты) располагаются в куче. Куча - это оперативная память вашего компьютера. Доступ к ней осуществляется медленнее, чем к стеку. Когда объект располагается в куче, то переменная хранит лишь адрес объекта. Этот адрес хранится в стеке. По адресу программа имеет доступ к самому объекту, все данные которого сохраняются в общем куске памяти (куче).
«Сборщик мусора» уничтожает объекты, располагающиеся в стеке, каждый раз, когда соответствующая переменная выходит за область видимости. [37] Таким образом, если вы объявляете локальную переменную в пределах функции, то объект будет помечен как объект для «сборки мусора».[38]
Следует подытожить: стек – это структура данных, работающая по принципу обратному очередности, доступ к стеку осуществляется намного быстрее чем к куче; куча – основная память программ, в которой хранятся ссылочные типы; сборщик мусора – автоматизированная система очистки памяти.
Приведение типов
Множество типов данных порождает проблемы при выполнении операций с операндами (участниками операций) различных типов.
Рассмотрим варианты присваивания значения переменной. Операция присваивания выполняется справа налево. Например,
int i,j;
i=j=5; равнозначно j=5; i=j;
При присваивании переменной одного типа значения константы или переменной другого типа (или инициализации переменной с помощью константы или другой переменной) происходит автоматическое приведение типов, то есть преобразование типа правого операнда в тип левого. В С# разрешено автоматическое приведение типов, как показано в таблице 2. В левом столбце указаны типы левого операнда, в верхней строке -типы правого операнда в выражении left=right;. На пересечении соответствующих строк и столбцов знаком + отмечены разрешенные сочетания. Смысл ограничения в том, что для предотвращения потери информации значение правого операнда необходимо разместить в области возможных значений левого. Компилятор фиксирует ошибки при присваивании. Переменные типа bool к числовым типам не приводятся.
В листинге 4 показаны наиболее интересные варианты присваивания, в том числе инициализация переменной типа long шестнадцатеричным числом и типа double числом в научной нотации. Результат исполнения программы приведен на рисунке 5. Следует обратить внимание на то, что при присваивании переменной числового типа значения типа char левому операнду передается код символа (выделенные ячейки в таблице 2).
Таблица 2 - Автоматическое приведение базовых типов при
присваивании[39]
left |
byte |
sbyte |
short |
ushort |
int |
uint |
long |
ulong |
char |
float |
double |
decimal |
bool |
byte |
+ |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
sbyte |
- |
+ |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
short |
+ |
+ |
+ |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
ushort |
+ |
- |
- |
+ |
- |
- |
- |
- |
- |
- |
- |
- |
- |
int |
+ |
+ |
+ |
+ |
+ |
- |
- |
- |
+ |
- |
- |
- |
- |
uint |
+ |
- |
- |
+ |
- |
+ |
- |
- |
+ |
- |
- |
- |
- |
long |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
- |
+ |
- |
- |
- |
- |
ulong |
+ |
- |
- |
+ |
- |
+ |
- |
+ |
+ |
- |
- |
- |
- |
char |
- |
- |
- |
- |
- |
- |
- |
- |
+ |
- |
- |
- |
- |
float |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
- |
- |
- |
double |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
- |
- |
decimal |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
- |
- |
+ |
- |
bool |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
- |
+ |
+ автоматическое приведение типа right к left
- запрет автоматического приведения типа right к left.
выделение кода символа
Листинг 4 - Автоматическое приведение типов при присваивании
/ Базовые типы. Автоматическое приведение типов при присваивании*/
using System;
namespace ConsoleApp4
{
class Program[40]
{
static void Main(string[] args)
{
byte @byte = 134;
sbyte @sbyte = -125;
short @short = -4318;
ushort @ushort = 15, ushort1;
int @int = 1, @int1;
uint @uint = 99, @uint1;
long @long = 0x7db3, @long1;
ulong @ulong = 99076, @ulong1;
float @float = -0.879f, @float1; ;
double @double = -15.66e+2, @double1;
decimal @decimal = 73m, @decimal1;
char @char='%';
bool @bool = true;
Console.WriteLine("ПЕРЕМЕННЫЕ БАЗОВЫХ ТИПОВ");
Console.Write(@byte + " " + @sbyte + " " + @short + " " + @ushort + " " + @int + " ");
Console.Write(@uint + " " + @long + " " + @ulong + " " + @float + " " + @double + " ");
Console.WriteLine(@decimal + " " + @char + " " + @bool + "\n");
Console.WriteLine("ПРИСВАИВАНИЕ");
short @short1 = @byte; Console.Write(@short1 + " ");
@short1 = @sbyte; Console.Write(@short1 + "\n");
@ushort1 = @byte; Console.Write(@ushort1 + "\n");
@int1 = @byte; Console.Write(@int1 + " ");
@int1 = @sbyte; Console.Write(@int1 + " ");
@int1 = @short; Console.Write(@int1 + " ");[41]
@int1 = @ushort; Console.Write(@int1 + " ");
@int1 = @char; Console.Write(@int1 + "\n");// код символа
@uint1 = @byte; Console.Write(@uint1 + " ");
@uint1 = @ushort; Console.Write(@uint1 + "\n");
@long1 = @byte; Console.Write(@long1 + " ");
@long1 = @sbyte; Console.Write(@long1 + " ");
@long1 = @short; Console.Write(@long1 + " ");
@long1 = @ushort; Console.Write(@long1 + " ");
@long1 = @int; Console.Write(@long1 + " ");
@long1 = @uint; Console.Write(@long1 + " ");
@long1 = @char; Console.Write(@long1 + "\n");//код символа
@ulong1 = @byte; Console.Write(@ulong1 + " ");
@ulong1 = @ushort; Console.Write(@ulong1 + " ");
@ulong1 = @uint; Console.Write(@ulong1 + " ");
@ulong1 = @char; Console.Write(@ulong1 + "\n");//код симв. float
@float1 = @char; Console.Write(@float1 + "\n");//код символа
@double1 = @char; Console.WriteLine(@double1 + "\n");/*код символа*/
@decimal1 = @char; Console.Write(@ulong1 + " ");/*код символа*/
@decimal1 = @byte; Console.Write(@ulong1 + " ");
@decimal1 = @sbyte; Console.Write(@decimal1 + " ");
@decimal1 = @short; Console.Write(@decimal1 + " ");
@decimal1 = @ushort; Console.Write(@decimal1 + " ");
@decimal1 = @long; Console.Write(@decimal1 + " ");
@decimal1 = @ulong; Console.WriteLine(@decimal1);
Console.WriteLine("THE END");
}
}
}[42]
Рисунок 5 - Результат исполнения программы из листинга 4
При выполнении многочленных арифметических операций производится автоматическое приведение операндов по умолчанию.
Если в одном выражении использованы переменные различных типов, то все входящие операнды приводятся к одному типу и это происходит по мере перехода от одной операции к другой.
Перечислим правила приведения типов для бинарных (с двумя операндами) операций:
если один операнд имеет тип decimal, то и второй преобразуется в decimal, но если второй операнд имеет тип double или float, результат будет ошибочным;
если один операнд имеет тип double, то второй преобразуется в тип double;
если один операнд имеет тип float, то второй преобразуется в тип float; [43]
если один операнд имеет тип ulong, то второй преобразуется в тип ulong, но если второй операнд имеет тип sbyte, short, int или long (то есть целочисленный тип со знаком), результат будет ошибочным;
если один операнд имеет тип long, то второй преобразуется в тип long;
если один операнд имеет тип uint, то второй преобразуется в тип uint, за исключением случаев, когда один операнд имеет тип uint, а второй имеет тип sbyte, short или int, то оба преобразуются в long;
если не было применено ни одно из перечисленных выше правил, оба операнда преобразуются в тип int.
Пункты правил приведения следует применять последовательно.
Правила автоматического приведения типов, надо заметить, достаточно громоздки и запутанны. Для практического использования удобна, как представляется, приведенная ниже таблица 3, где в левом столбце и второй сверху строке записаны исходные типы операндов, а на пересечении строк и столбцов - тип приведенных операндов и соответственно результата операции с ними.
При всем этом нужно следить, переменной какого типа реально в программе присваивается результат операции. Он должен соответствовать или сочетаться (см. табл. 2) по типу с результатом, то есть с тем типом, в котором после приведения представлены оба операнда. Например:
@ulong=@ulong+@int;/*правое слагаемое автоматически приводится к типу ulong*/
@long=@uint+@short;/*оба слагаемых автоматически приводится к типу long*/
@int=@byte+@byte;/*оба слагаемых автоматически приводится к типу int*/
@double=@byte+@byte+@double1;/*первое и второе слагаемые приводятся к типу int, складываются, результат приводится к @double*/[44]
@long=@char+@char1+@uint;/*первое и второе слагаемые приводятся к типу int, их сумма и третье слагаемое приводятся к типу long, общая сумма присваивается переменной типа long*/
@ulong=@char+@char1+@uint;/*вариант ошибочен, присваивание переменной типа ulong значения переменной типа long (общей суммы) запрещено*/
Компьютер фиксирует ошибки как при присваивании значений отдельным переменным, так и при присваивании результатов операций после автоматического приведения операндов.
Следует также иметь ввиду, что окончательный (или промежуточный) результат может не поместиться в области значений формально правильно выбранного типа. Такая ошибка при компиляции не фиксируется.