0Pricing
C# Academy · Урок

Равенство значений и деконструкция

Разберитесь в структурном равенстве записей, переопределяйте Equals/GetHashCode и используйте шаблоны деконструкции.

«Равенство значений и деконструкция» — бесплатный урок C# Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.

Подробно о равенстве записей

Записи автоматически создают Equals, GetHashCode и операторы ==/!=, которые структурно сравнивают все свойства. Понимание этого механизма помогает правильно использовать записи и при необходимости переопределять его.

Как работает автоматически создаваемое равенство

Компилятор создаёт код, сравнивающий каждое свойство с помощью EqualityComparer<T>.Default. Для свойств ссылочных типов это их собственный Equals, а не ссылочное равенство.

public record Address(string Street, string City);
public record Person(string Name, Address HomeAddress);

var a = new Person("Alice", new Address("123 Main", "NYC"));
var b = new Person("Alice", new Address("123 Main", "NYC"));

Console.WriteLine(a == b); // True — deep structural equality
// Equality checks:
// Name: "Alice" == "Alice" ✓
// HomeAddress: compares Address properties recursively ✓

Переопределение автоматически созданного равенства

В записи можно переопределить Equals и GetHashCode, если требуется собственная логика равенства — например, сравнение только части свойств.

public record Customer(int Id, string Name, string Email)
{
    // Consider customers equal if they have the same ID,
    // regardless of name or email changes
    public virtual bool Equals(Customer? other)
        => other is not null && Id == other.Id;

    public override int GetHashCode() => Id.GetHashCode();
}

Записи в коллекциях

Поскольку записи реализуют IEquatable<T> со структурным равенством, они корректно работают как ключи словарей, в HashSets, а также с Distinct и GroupBy из LINQ.

public record Tag(string Name, string Color);

var tags = new List<Tag>
{
    new("C#", "blue"), new("C#", "blue"), new("dotnet", "purple")
};

var unique = tags.Distinct().ToList(); // [Tag(C#, blue), Tag(dotnet, purple)]

var set = new HashSet<Tag>(tags);
set.Contains(new Tag("C#", "blue")); // true

Деконструирование: основы

Позиционные записи автоматически создают метод Deconstruct с параметрами out для каждого позиционного свойства. Это позволяет распаковывать их подобно кортежам.

public record Point(double X, double Y);
public record RGB(byte R, byte G, byte B);

var p = new Point(3.0, 4.0);
var (x, y) = p; // deconstruct
Console.WriteLine($"{x}, {y}");

var color = new RGB(255, 128, 0);
var (r, g, b) = color;
Console.WriteLine($"R={r}, G={g}, B={b}");

Деконструирование при сопоставлении с образцом

Деконструирование записей в выражениях switch позволяет выполнять мощное позиционное сопоставление с образцом.

public record Point(int X, int Y);

string Classify(Point p) => p switch
{
    (0, 0)           => "Origin",
    (int x, 0)       => $"X-axis at {x}",
    (0, int y)       => $"Y-axis at {y}",
    (int x, int y) when x == y => $"Diagonal at {x}",
    (int x, int y)   => $"Point ({x}, {y})"
};

Console.WriteLine(Classify(new Point(0, 0)));  // Origin
Console.WriteLine(Classify(new Point(3, 3)));  // Diagonal at 3

Пользовательские методы Deconstruct

В непозиционные записи (и любые классы) можно вручную добавить метод Deconstruct. Методы расширения также позволяют добавить деконструирование существующим типам.

public class Temperature
{
    public double Celsius { get; }
    public Temperature(double c) => Celsius = c;

    // Manual Deconstruct
    public void Deconstruct(out double celsius, out double fahrenheit)
    {
        celsius    = Celsius;
        fahrenheit = Celsius * 9 / 5 + 32;
    }
}

var t = new Temperature(100);
var (c, f) = t;
Console.WriteLine($"{c}°C = {f}°F"); // 100°C = 212°F

Игнорирование частей деконструирования

Используйте отбрасываемые значения (_), чтобы игнорировать ненужные части деконструирования, подобно отбрасываемым значениям кортежей.

public record Order(int Id, string Customer, decimal Total, DateTime Date);

var order = new Order(42, "Alice", 99.99m, DateTime.UtcNow);

// Only want Id and Total
var (id, _, total, _) = order;
Console.WriteLine($"Order {id}: ${total}");

// In switch: ignore some fields
string Classify(Order o) => o switch
{
    (_, _, > 1000, _) => "High value",
    (_, _, > 100, _)  => "Medium value",
    _                 => "Low value"
};

Сопоставление свойств и позиционное сопоставление

Записи поддерживают позиционные шаблоны (по позиции) и шаблоны свойств (по имени). Шаблоны свойств лучше читаются в записях с большим количеством свойств.

public record Order(int Id, string Status, decimal Total);

// Positional pattern:
string DescribePos(Order o) => o switch
{
    (_, "Shipped", > 100) => "High-value shipped",
    _                     => "Other"
};

// Property pattern (more readable):
string DescribeProp(Order o) => o switch
{
    { Status: "Shipped", Total: > 100 } => "High-value shipped",
    { Status: "Cancelled" }             => "Cancelled",
    _                                   => "Other"
};

Практический пример: шаблон типа Result

Тип результата в стиле размеченного объединения с использованием записей и деконструирования — функциональный подход к обработке ошибок.

public abstract record Result<T>;
public record Success<T>(T Value) : Result<T>;
public record Failure<T>(string Error) : Result<T>;

Result<Order> PlaceOrder(CreateOrderRequest req)
{
    if (req.Quantity <= 0)
        return new Failure<Order>("Quantity must be positive");
    var order = CreateOrder(req);
    return new Success<Order>(order);
}

// Consuming:
var result = PlaceOrder(request);
switch (result)
{
    case Success<Order>(var order):
        return Results.Created($"/orders/{order.Id}", order);
    case Failure<Order>(var error):
        return Results.BadRequest(error);
}

Быстрая проверка

Что делает отбрасываемое значение (_) в выражении деконструирования?

Повторение: равенство по значению и деконструирование

Основные выводы:

  • Автоматически создаваемое равенство структурно и рекурсивно сравнивает все свойства
  • Переопределяйте Equals/GetHashCode у записей для реализации пользовательской логики идентичности
  • Записи корректно работают как элементы HashSet и ключи словаря
  • Для позиционных записей автоматически создаётся Deconstruct — используйте var (a, b) = record
  • Отбрасываемые значения (_) позволяют игнорировать ненужные позиционные значения при деконструкции
  • Сопоставление со свойствами ({Status: "X"}) читается лучше позиционного сопоставления в сложных записях

Часто задаваемые вопросы

Урок «Равенство значений и деконструкция» бесплатный?

Да — полный текст урока «Равенство значений и деконструкция» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.

Чему я научусь в уроке «Равенство значений и деконструкция»?

Разберитесь в структурном равенстве записей, переопределяйте Equals/GetHashCode и используйте шаблоны деконструкции. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C# Academy?

Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Равенство значений и деконструкция»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C# Academy?

Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Типы записей: основы и синтаксис
  2. Неизменяемость с init и with
  3. Равенство значений и деконструкция
  4. Записи в предметно-ориентированном проектировании
← Назад к C# Academy