Перейти к основному содержимому

5.05. Типы данных

Разработчику Архитектору

Типы данных

Типы данных в C#

Типы данных – это фундамент любого языка программирования.

В C# типы делятся на несколько категорий:

  • простые (примитивные) типы;
  • ссылочные типы;
  • структуры или значимые типы (value types);
  • кортежи;
  • пользовательские типы.

★ Простые типы:

ТипОписаниеРазмер
intЦелое число4 байта
longДлинное целое8 байт
shortКороткое целое2 байта
byteЧисло от 0 до 2551 байт
float, double, decimalЧисла с плавающей точкойразный
charСимвол Unicode2 байта
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 }
};