5.05. Типы данных
Типы данных
Типы данных в C#
★ Типы данных – это фундамент любого языка программирования.
В C# типы делятся на несколько категорий:
- простые (примитивные) типы;
- ссылочные типы;
- структуры или значимые типы (value types);
- кортежи;
- пользовательские типы.
★ Простые типы:
| Тип | Описание | Размер |
|---|---|---|
int | Целое число | 4 байта |
long | Длинное целое | 8 байт |
short | Короткое целое | 2 байта |
byte | Число от 0 до 255 | 1 байт |
float, double, decimal | Числа с плавающей точкой | разный |
char | Символ Unicode | 2 байта |
string | Строка символов | динамический |
bool | Логическое значение (true / false) | — |
★ Ссылочные и значимые типы
Ссылочные и значимые типы отличаются способом хранения данных в памяти и поведением при присваивании, передаче в методы и управлении ресурсами.
★ Значимые типы (Value Types) хранят непосредственно данные, а не ссылку на них. Обычно они размещаются в стеке, но могут быть частью объектов в куче, если входят в состав ссылочного типа.
Примеры:
int age = 30;
bool isStudent = true;
char grade = 'A';
DateTime birthDate = new DateTime(1995, 5, 20);
Примитивные (простые) типы также относятся к значимым типам (int, float, double, char и т.д.).
Помимо них, есть:
- перечисления (enum):
enum Days { Monday, Tuesday, Wednesday }
- структуры (struct):
struct Point {
public int X;
public int Y;
}
- ValueTuple:
var person = (name: "Alice", age: 25);
При присваивании одного экземпляра значимого типа другому, копируются данные целиком:
int a = 10;
int b = a; // b получает копию значения a
a = 20;
Console.WriteLine(b); // Выведет 10
Особенности значимых типов:
- более быстрые операции за счёт хранения в стеке;
- не поддерживают множественное наследование;
- не могут быть null, если не используются с модификатором?
Пример nullable-типа:
int? nullableInt = null;
Nullable-версии типов могут принимать значение null, а внутри реализуются через обобщённую структуру System.Nullable<T>.
Число
Число (int, double, decimal).
Значимые типы. decimal используется для финансовых вычислений (более точный, чем double).
Простой пример:
int age = 30;
Сложный пример:
decimal taxAmount = subtotal * (region switch {
"EU" => 0.2m,
"US" => stateTaxRates[state],
"ASIA" => 0.15m,
_ => 0.0m
});
Использование выражения switch для определения налоговой ставки в зависимости от региона. Результат умножается на сумму. Тип decimal гарантирует точность при финансовых операциях.
Булево
Булево (bool).
Примитивный тип, аналогичный Java.
Простой пример:
bool isActive = true;
Сложный пример:
bool isValid = !string.IsNullOrEmpty(email) &&
email.Contains("@") &&
allowedDomains.Any(domain => email.EndsWith(domain)) &&
!blacklistedEmails.Contains(email);
Проверка валидности email включает несколько условий: не null/empty, наличие символа @, соответствие домену и отсутствие в чёрном списке. Используется LINQ (Any, Contains).
Ссылочные типы
★ Ссылочные типы (Reference Types) содержат ссылку (адрес) на область памяти в куче, где находятся реальные данные.
Примеры:
string name = "John";
object obj = 42;
List<int> numbers = new List<int>();
Категории ссылочных типов:
- классы (class):
class Person {
public string Name { get; set; }
}
- интерфейсы (interface):
interface ILogger {
void Log(string message);
}
- делегаты (delegate):
delegate void Notify(string message);
- массивы (array):
int[] numbers = new int[] {1, 2, 3};
- тип object (базовый тип для всех типов в .NET);
- тип string (строка), неизменяемая (immutable) и имеет особое поведение.
При присваивании одного экземпляра ссылочного типа другому, копируется ссылка, а не сам объект:
Person p1 = new Person { Name = "Alice" };
Person p2 = p1; // p2 указывает на тот же объект
p1.Name = "Bob";
Console.WriteLine(p2.Name); // Выведет "Bob"
Особенности ссылочных типов:
- требуют управления памятью через Garbage Collector (GC);
- могут быть null;
- поддерживают наследование и полиморфизм;
- могут иметь сложную структуру и включать другие типы.
Строка
Строка (string).
Неизменяемый ссылочный тип. Богатый набор методов и поддержка интерполяции. Простой пример:
string name = "Анна";
Сложный пример:
string report = $@"
Отчёт за {DateTime.Now:MMMM yyyy}
-----------------------------------
Пользователь: {user.Name?.Trim() ?? "Не указан"}
Роль: {(user.Role.HasValue ? Enum.GetName(typeof(Role), user.Role) : "N/A")}
Баланс: {user.Balance:C}
Статус: {(user.IsActive ? "Активен" : "Заблокирован")}
".Trim();
Интерполяция с условными выражениями, безопасным доступом к свойствам (?.), форматированием валюты (:C) и именем перечисления. Результат — многострочный отчёт.
Объект
Объект (class, record). Ссылочные типы. record — неизменяемые объекты (с C# 9).
Простой пример:
var person = new { Name = "Иван", Age = 28 };
Сложный пример:
var employees = dbContext.Employees
.Where(e => e.DepartmentId == deptId && e.Salary > minSalary)
.Select(e => new EmployeeSummary(
e.Id,
$"{e.FirstName} {e.LastName}".Trim(),
e.Position,
e.Salary * (1 + bonusPercentage)
))
.ToList();
LINQ-запрос к базе данных, фильтрация и проекция в DTO-объект с вычислением бонуса. Демонстрирует интеграцию с ORM (например, Entity Framework).
Массив
Массив (array).
Фиксированная коллекция одного типа.
Простой пример:
int[] numbers = { 1, 2, 3 };
Сложный пример:
int[,] distanceMatrix = new int[cities.Length, cities.Length];
for (int i = 0; i < cities.Length; i++)
{
for (int j = 0; j < cities.Length; j++)
{
distanceMatrix[i, j] = (int)CalculateDistance(cities[i], cities[j]);
}
}
Двумерный массив расстояний между городами. Заполняется через вложенный цикл и вызов функции. Типичный случай для задач маршрутизации.
Делегат / функция
Делегат / Функция (Func, Action).
Типы-обёртки для функций. Func<T>, TResult>, Action<T>.
Простой пример:
Func<string, string> greet = name => $"Привет, {name}!";
Сложный пример:
Func<User, bool> isPremium = u =>
u.SubscriptionLevel == Subscription.Premium &&
u.LastLogin > DateTime.Now.AddDays(-30) &&
u.TotalPurchases.Sum(p => p.Amount) > 1000;
var premiumUsers = allUsers.Where(isPremium).ToList();
Делегат Func<User, bool> инкапсулирует правило определения премиум-пользователя. Затем используется в LINQ-запросе. Позволяет переиспользовать логику проверки.
Когда и какой тип данных использовать?
- для простых данных (часто используемых) – значимый тип;
- логическая группа данных – struct;
- сложные объекты, бизнес-логика, коллекции – class;
- константы состояния – enum;
- безопасность и простота – readonly struct.
Виды массивов
Массив – упорядоченная коллекция элементов одного типа.
Одномерный массив:
int[] numbers = { 1, 2, 3 };
Многомерный массив:
int[,] matrix = {
{ 1, 2 },
{ 3, 4 }
};
Jagged-массив (массив массивов):
int[][] rows = new int[][] {
new int[] { 1, 2 },
new int[] { 3, 4, 5 }
};