Язык программирования C#9 и платформа .NET5
Console.WriteLine("Disposed!"); // Экземпляр освобожден!}}Теперь поместите в конце операторов верхнего уровня приведенный ниже код, предназначенный для создания и освобождения новой структуры:
var s = new DisposableRefStruct(50, 60);s.Display();s.Dispose();На заметку! Темы времени жизни и освобождения объектов раскрываются в главе 9.
Чтобы углубить понимание выделения памяти в стеке и куче, необходимо ознакомиться с отличиями между типами значений и ссылочными типами .NET Core.
Типы значений и ссылочные типы
На заметку! В последующем обсуждении типов значений и ссылочных типов предполагается наличие у вас базовых знаний объектно-ориентированного программирования. Если это не так, тогда имеет смысл перейти к чтению раздела "Понятие типов С#, допускающих
" далее в главе и возвратиться к настоящему разделу после изучения глав 5 и 6.nullВ отличие от массивов, строк и перечислений структуры C# не имеют идентично именованного представления в библиотеке .NET Core (т.е. класс вроде
отсутствует), но они являются неявно производными от абстрактного классаSystem.Structure. Роль классаSystem.ValueTypeзаключается в обеспечении размещения экземпляра производного типа (например, любой структуры) в стеке, а не в куче с автоматической сборкой мусора. Выражаясь просто, данные, размещаемые в стеке, могут создаваться и уничтожаться быстро, т.к. время их жизни определяется областью видимости, в которой они объявлены. С другой стороны, данные, размещаемые в куче, отслеживаются сборщиком мусора .NET Core и имеют время жизни, которое определяется многими факторами, объясняемыми в главе 9.System.ValueTypeС точки зрения функциональности единственное назначение класса
— переопределение виртуальных методов, объявленных в классеSystem.ValueType, с целью использования семантики на основе значений, а не ссылок. Вероятно, вы уже знаете, что переопределение представляет собой процесс изменения реализации виртуального (или возможно абстрактного) метода, определенного внутри базового класса. Базовым классом дляSystem.ObjectявляетсяValueType. В действительности методы экземпляра, определенные вSystem.Object, идентичны методам экземпляра, которые определены вSystem.ValueType:System.Object// Структуры и перечисления неявно расширяют класс System.ValueType.public abstract class ValueType : object{public virtual bool Equals(object obj);public virtual int GetHashCode();public Type GetType();public virtual string ToString();}Учитывая, что типы значений применяют семантику на основе значений, время жизни структуры (что относится ко всем числовым типам данных (
,int), а также к любому перечислению или структуре) предсказуемо. Когда переменная типа структуры покидает область определения, она немедленно удаляется из памяти:float// Локальные структуры извлекаются из стека,// когда метод возвращает управление.static void LocalValueTypes(){// Вспомните, что int - на самом деле структура System.Int32.int i = 0;// Вспомните, что Point - в действительности тип структуры.Point p = new Point();} // Здесь i и р покидают стек!Использование типов значений ссылочных типов и операции присваивания
Когда переменная одного типа значения присваивается переменной другого типа значения, выполняется почленное копирование полей данных. В случае простого типа данных, такого как
, единственным копируемым членом будет числовое значение. Однако для типаSystem.Int32в новую переменную структуры будут копироваться значения полейPointиX. В целях демонстрации создайте новый проект консольного приложения по имениYи скопируйте предыдущее определениеFunWithValueAndReferenceTypesв новое пространство имен, после чего добавьте к операторам верхнего уровня следующую локальную функцию:Point// Присваивание двух внутренних типов значений дает// в результате две независимые переменные в стеке.static void ValueTypeAssignment(){Console.WriteLine("Assigning value types\n");Point p1 = new Point(10, 10);Point p2 = p1;// Вывести значения обеих переменных Point.p1.Display();p2.Display();// Изменить pl.X и снова вывести значения переменных.// Значение р2.Х не изменилось.p1.X = 100;Console.WriteLine("\n=> Changed p1.X\n");p1.Display();p2.Display();}Здесь создается переменная типа
, которая присваивается другой переменной типаPoint(p1). ПосколькуPoint(р2)— тип значения, в стеке находятся две копииPoint, каждой из которых можно манипулировать независимым образом. Поэтому при изменении значенияPointзначениеp1.Xостается незатронутым:р2.X