Информация о полезной статье
RAM and Data Types (Типы данных и память)
Структура типов данных C#
RAM and Data Types
│
├─ Stack (value types - типы значений)
│ ├─ enum (перечисления)
│ ├─ Tuple (картежи)
│ ├─ Nullable value types [C# 2.0+]
│ └─ Structure (структуры)
│ ├─ struct
│ ├─ ref struct [C# 7.2+]
│ ├─ bool
│ ├─ char
│ └─ Numeric types (числовые типы)
│ ├─ Signed Integers (целочисленные отрицательные)
│ │ ├─ sbyte
│ │ ├─ short
│ │ ├─ int
│ │ └─ long
│ ├─ Unsigned Integers (целочисленные положительные)
│ │ ├─ byte
│ │ ├─ ushort
│ │ ├─ uint
│ │ └─ ulong
│ ├─ System-dependent Integers (зависимые от разряда системы)
│ │ ├─ nint
│ │ └─ nuint
│ └─ Floating-point Numbers (с плавающей точкой)
│ ├─ float
│ ├─ double
│ └─ decimal
│
└─ Heap (reference types - типы ссылок)
├─ object
├─ class
├─ record [C# 9+]
├─ interface
├─ delegate
├─ dynamic
├─ string
└─ array (массив)
└─ Nullable reference types [C# 8+]
(Unsafe контекст)
└─ Pointer types (указатели) - переменные в Stack, указывают на любую область памяти
├─ Указатели на типы значений: int*, long*, short*, byte*, bool*, char*, float*, double*, decimal*
├─ void* - универсальный указатель (может указывать на любой тип)
│ └─ Могут указывать на: Stack, Heap, неуправляемую память
└─ Function pointers (delegate*) - указатели на функции
Signed Integers (целочисленные отрицательные):
sbyte- 8 бит, -128 до 127short- 16 бит, -32,768 до 32,767int- 32 бит, -2,147,483,648 до 2,147,483,647long- 64 бит, -9,223,372,036,854,775,808 до 9,223,372,036,854,775,807
Unsigned Integers (целочисленные положительные):
byte- 8 бит, 0 до 255ushort- 16 бит, 0 до 65,535uint- 32 бит, 0 до 4,294,967,295ulong- 64 бит, 0 до 18,446,744,073,709,551,615
System-dependent Integers (зависимые от разряда системы):
nint- нативное целое число (размер зависит от платформы)nuint- нативное беззнаковое целое число
Floating-point Numbers (с плавающей точкой):
float- 32 бит, одинарная точностьdouble- 64 бит, двойная точностьdecimal- 128 бит, высокая точность для финансовых расчетов
Важные нюансы размещения в памяти
1. Типы значений (Value Types) не всегда в Stack
Важно: Типы значений хранятся в Stack только когда они являются локальными переменными.
- В Stack: локальные переменные, параметры методов
- В Heap: поля классов/структур, элементы массивов
class MyClass
{
int value; // В Heap (как часть объекта MyClass)
}
void Method()
{
int local = 42; // В Stack (локальная переменная)
int[] array = new int[10];
array[0] = 1; // В Heap (элемент массива)
}
2. Tuple vs ValueTuple
Tuple(class) - ссылочный тип, хранится в Heap (устаревший, не рекомендуется)ValueTuple(struct) - тип значения, хранится в Stack (рекомендуется)
// ValueTuple - в Stack
(int, string) tuple1 = (1, "hello");
// Tuple - в Heap (устаревший)
Tuple<int, string> tuple2 = Tuple.Create(1, "hello");
3. Boxing и Unboxing
Boxing (преобразование-упаковка)
При перемещении данных из типов значения в ссылочные типы, то есть из стека в кучу.
Преобразование: int -> object
int number = 420;
object obj = number;
UnBoxing (преобразование-распаковка)
Обратное действие из ссылочных типов в тип значения, то есть из кучи в стек.
Преобразование: object -> int
object obj = 420;
int number = (int) obj;
Производительность: Boxing/Unboxing создает копии и влияет на производительность!
4. ref struct ограничения
ref struct должны находиться только в Stack и имеют ограничения:
- Не могут быть полями обычных классов или структур
- Не могут быть элементами массивов
- Не могут быть generic параметрами (кроме других ref struct)
- Не могут реализовывать интерфейсы
- Используются для высокопроизводительных типов:
Span<T>,ReadOnlySpan<T>
5. String Interning (интернирование строк)
Строки в C# могут быть интернированы - одинаковые строковые литералы могут указывать на один объект в Heap:
string s1 = "Hello";
string s2 = "Hello";
// s1 и s2 могут указывать на один объект (зависит от компилятора и runtime)
bool same = ReferenceEquals(s1, s2); // Может быть true
6. Nullable Value Types
Важно: Nullable<T> (или T?) - это структура, которая хранит:
- Значение типа
T - Флаг
HasValue(было ли присвоено значение)
Размер: размер базового типа + 1 байт (обычно выравнивается до размера базового типа + sizeof(bool))
int? nullable = null; // В Stack: структура Nullable<int> с HasValue = false
int? nullable2 = 42; // В Stack: структура Nullable<int> с HasValue = true, Value = 42
7. Массивы - элементы в Heap
Массив - ссылочный тип, хранится в Heap. Элементы массива тоже хранятся в Heap (даже если это типы значений):
int[] numbers = new int[10]; // Массив в Heap
numbers[0] = 42; // Элемент массива тоже в Heap (не в Stack!)
8. Dynamic - это object
Тип dynamic в runtime является object:
- Проверка типов откладывается до выполнения (runtime)
- Может привести к ошибкам времени выполнения
- Используется для работы с COM, JSON, динамическими языками
9. Размер указателей
- В 32-битных системах: 4 байта
- В 64-битных системах: 8 байт
- Размер не зависит от типа, на который указывает указатель
10. ref локальные переменные и ref return
С C# 7.0 можно возвращать ссылки (не копии):
ref int GetReference(int[] array) => ref array[0];
int[] arr = { 1, 2, 3 };
ref int first = ref GetReference(arr);
first = 100; // Изменит arr[0] напрямую (без копирования)
Важно: ref переменные - это алиасы (псевдонимы), не новые переменные в памяти.