Типы-значения (value type) в C#

Урок посвящен типам-значениям данных и их особенностям и правилам использования.

В рамках этого урока мы познакомимся поближе с типами-значениями.

О типах значениях важно помнить следующее:

  • экземпляры этих типов располагаются в стеке, но если они являются частью ссылочного типа, то в куче;
  • они не обрабатываются сборщиком мусора (только вместе с объектом, в состав которого могут входить);
  • как только метод (блок кода), в котором они объявлены, завершается, экземпляры удаляются из памяти;
  • если вы присваиваете значение одной переменной типа-значения другой, то создается новый экземпляр, в который копируются все поля из исходного объекта;
  • тип-значение не может быть базовым типом, то есть от него нельзя наследоваться;
  • при сравнении экземпляров типов через оператор == (или метод Equals()) сравниваются значения полей, а не ссылки.

К типам-значениям относятся:

  • группа из набора примитивных типов (числа, boolchar);
  • перечисления (enum);
  • структуры (struct);
  • типы значений, допускающие null;
  • типы значений кортежей (ValueTuple);
  • структуры записи (struct record).

В рамках урока подробно остановимся на примитивных типах-значениях (про другие типы более детально расскажем позже).

К примитивным типам-значениям относятся:

  • целочисленные типы;
  • типы с плавающей точкой;
  • тип bool для представления логических значений (true и false);
  • тип char для представления символьных значений.

Рассмотрим, что происходит при создании переменной типа-значения, на примере переменной типа int.

int n = 3;

При выполнении этой строки в стеке выделяется память под переменную num.

При модификации значения переменной, в памяти (в соответствующей ячейке) изменится содержимое.

При завершении работы блока кода (метода), память, выделенная под локальные переменные, очищается.

В предыдущем уроке, посвященном ссылочным типам, мы выяснили, что при присваивании значения ссылочного типа, происходит копирование ссылки на объект, который располагается в памяти (куче), при этом сам объект остается тем же.

Person p1 = new Person() { Name = "John" }; // Person - это класс

Person p2 = p1;

При присваивании типов-значений происходит создание нового экземпляра типа и копирование значений свойств (полей) из исходного значения в новое. Все это будет храниться в стеке.

Создадим объект тип Point, присвоим его другому объекту:

var pnt1 = new Point() { X = 1.2, Y = 3.4 }; // Point - это структура

var pnt2 = pnt1;

Console.WriteLine($"{pnt1.X}, {pnt1.Y}"); // 1.2, 3.4
Console.WriteLine($"{pnt2.X}, {pnt2.Y}"); // 1.2, 3.4

Изменим значение поля X у pnt1:

pnt1.X = 5.6;

Console.WriteLine($"{pnt1.X}, {pnt1.Y}"); // 5.6, 3.4
Console.WriteLine($"{pnt2.X}, {pnt2.Y}"); // 1.2, 3.4

При этом значение полей у pnt2 не измениться, так как эти переменные связаны с разными областями памяти в стеке.

При сравнении объектов ссылочного типа между собой через метод Equals(), если у соответствующего класса он не переопределен, сравниваются ссылки, то есть проверяется: указывают ли переменные на один и тоже объект в памяти, таким образом, проверяется тождественность объектов. Если метод Equals() переопределен, то результат сравнения зависит от  заложенной в него логики.

Сравнение экземпляров типов-значений в случае если  метод Equals() не переопределен, выполняется как сравнение значений свойств (полей).

Создадим набор переменных типа Point и сравним их между собой:

var a = new Point() { X = 1.2, Y = 3.4 };
var b = new Point() { X = 1.2, Y = 3.4 };
var c = new Point() { X = 5.6, Y = 3.4 };

Console.WriteLine(a.Equals(b)); // True
Console.WriteLine(a.Equals(c)); // False

Если Вы хотите больше узнать про язык C#, приглашаем Вас на наш курс “C#. Базовый уровень“.

Добавить комментарий

Ваш адрес email не будет опубликован.