- •А.А. Волосевич
- •2. Базовые технологии платформы .Net 5
- •2. Базовые технологии платформы .Net 4
- •2. Базовые технологии платформы .Net
- •2.1. Работа с Числами
- •2.2. Дата и время
- •2.3. Работа со строками и текстом
- •2.4. Преобразование информации
- •2.5. Отношения равенства и порядка
- •Сравнение для выяснения равенства
- •Сравнение для выяснения порядка
- •2.6. Жизненный цикл объектов
- •Алгоритм «сборки мусора»
- •Финализаторы и интерфейс iDisposable
- •2.7. Перечислители и итераторы
- •2.8. Интерфейсы стандартных коллекций
- •2.9. Массивы и класс system.Array
- •2.10. Типы для работы с коллекциями-списками
- •2.11. Типы для работы с коллекциями-множествами
- •2.12. Типы для работы с коллекциями-словарями
- •2.13. Типы для создания пользовательских коллекций
- •2.14. Технология linq to objects
- •1. Оператор условия Where().
- •2. Операторы проекций.
- •3. Операторы упорядочивания.
- •4. Оператор группировки GroupBy().
- •5. Операторы соединения.
- •6. Операторы работы с множествами.
- •7. Операторы агрегирования.
- •8. Операторы генерирования.
- •9. Операторы кванторов и сравнения.
- •10. Операторы разбиения.
- •11. Операторы элемента.
- •12. Операторы преобразования.
- •2.15. Работа с объектами файЛовой системы
- •2.16. Ввод и вывод информации
- •Потоки данных и декораторы потоков
- •2. Классы для работы с потоками, связанными с хранилищами.
- •3. Декораторы потоков.
- •4. Адаптеры потоков.
- •Адаптеры потоков
- •2.17. Основы xml
- •2.18. Технология linq to xml
- •Создание, сохранение, загрузка xml
- •Запросы, модификация и трансформация xml
- •Пространства имен xml
- •2.19. ДОполнительные возможности обработки xml
- •2.20. Сериализация
- •Сериализация времени выполнения
- •Сериализация контрактов данных
- •2.21. Состав и взаимодействие сборок
- •2.22. Метаданные и получение информации о типах
- •2.23. Позднее связывание и кодогенерация
- •2.24. Динамические типы
- •2.25. Атрибуты
- •2.26. Файлы конфигуРации
- •2.27. Основы мНогопоточноГо программирования
- •2.28. Синхронизация потоков
- •2.29. Библиотека параллельных расширений
- •Параллелизм на уровне задач
- •Параллелизм при императивной обработке данных
- •Параллелизм при декларативной обработке данных
- •Обработка исключений и отмена выполнения задач
- •Коллекции, поддерживающие параллелизм
- •2.30. Асинхронный вызов методов
- •2.31. Процессы и домены
- •2.32. Безопасность
- •Разрешения на доступ
- •Изолированные хранилища
- •Криптография
- •2.33. Диагностика
2.5. Отношения равенства и порядка
Платформа .NET и язык C# предлагают несколько стандартных протоколов для выяснения равенства и порядка объектов.
Сравнение для выяснения равенства
Наиболее общий подход при реализации проверки равенства заключается в переопределении виртуального метода Equals() класса object. Базовая версия этого метода использует равенство ссылок. Тип System.ValueType перекрывает Equals(), чтобы реализовать равенство по значению, то есть проверку на совпадение всех соответствующих полей двух переменных типа значения.
Основными причинами перекрытия Equals() в пользовательском типе являются: особая семантика равенства; перенос на ссылочный тип равенства по значению; необходимость ускорения проверки на равенство для типа значения. Перекрытая версия Equals() должна удовлетворять следующим требованиям1:
-
x.Equals(x) == true.
-
x.Equals(y) == y.Equals(x).
-
(x.Equals(y) && y.Equals(z)) == true x.Equals(z) == true.
-
Вызовы метода x.Equals(y) возвращают одинаковое значение до тех пор, пока x и y остаются неизменными.
-
x.Equals(null) == false, если x != null.
-
Метод Equals() не должен генерировать исключений.
В качестве примера перекрытия Equals() рассмотрим неизменяемый класс Area для представления информации о прямоугольной области. В классе Area реализована особая семантика равенства:
public class Area
{
public readonly int Height;
public readonly int Width;
public Area(int height, int width)
{
Height = height;
Width = width;
}
public override bool Equals(object obj)
{
Area other = obj as Area;
if (other == null)
{
return false;
}
return Height == other.Height && Width == other.Width
|| Height == other.Width && Width == other.Height;
}
public override int GetHashCode()
{
return Height > Width ? Height * 37 + Width :
Width * 37 + Height;
}
}
Area a1 = new Area(5, 10);
Area a2 = new Area(10, 5);
Console.WriteLine(a1.Equals(a2)); // True
Console.WriteLine(a1 == a2); // False
Заметим, что сравнение a1 == a2 даёт false, так как операция == не была перекрыта. Кроме этого, будь Area структурой, вызов a1.Equals(a2) привёл бы к операции упаковки для a2. Чтобы избежать ненужной упаковки, тип может дополнительно к перекрытию Equals() реализовать интерфейс IEquatable<T>:
public interface IEquatable<T>
{
bool Equals(T other);
}
Перекрытие операции == и реализацию IEquatable<T> рассмотрим на примере структуры Area:
public struct Area : IEquatable<Area>
{
public readonly int Height;
public readonly int Width;
public Area(int height, int width)
{
Height = height;
Width = width;
}
public bool Equals(Area other)
{
return Height == other.Height && Width == other.Width
|| Height == other.Width && Width == other.Height;
}
public override bool Equals(object other)
{
if (other is Area)
{
return Equals((Area) other);
}
return false;
}
public override int GetHashCode()
{
return Height > Width ? Height * 37 + Width :
Width * 37 + Height;
}
public static bool operator ==(Area a1, Area a2)
{
return a1.Equals(a2);
}
public static bool operator !=(Area a1, Area a2)
{
return !a1.Equals(a2);
}
}
Базовые типы значений платформы .NET (включая структуры для представления времени) реализуют интерфейс IEquatable<T> и перекрывают операции == и !=. Тип string дополнительно содержит перегруженную версию Equals(), позволяющую выполнить сравнение, учитывающее региональные стандарты или нечувствительное к регистру. Эта версия принимает аргумент-перечисление StringComparison со следующими элементами:
-
CurrentCulture ‑ сравнение в алфавите текущего регионального стандарта;
-
CurrentCultureIgnoreCase ‑ сравнение в алфавите текущего регионального стандарта без учёта регистра символов;
-
InvariantCulture ‑ сравнение в алфавите инвариантной культуры;
-
InvariantCultureIgnoreCase ‑ сравнение в алфавите инвариантной культуры без учёта регистра;
-
Ordinal – порядковое сравнение, при котором символы интерпретируются как числа (коды в UTF-16);
-
OrdinalIgnoreCase ‑ порядковое сравнение без учёта регистра.
Если создатель типа не реализовал эффективный метод проверки равенства, этот недостаток можно восполнить, применив подходящий подключаемый интерфейс. Интерфейсы System.Collections.Generic.IEqualityComparer<T> и System.Collections.IEqualityComparer позволяют организовать проверку объектов на равенство и вычисление хэш-кода объекта:
public interface IEqualityComparer
{
bool Equals(object x, object y);
int GetHashCode(object obj);
}
public interface IEqualityComparer<in T>
{
bool Equals(T x, T y);
int GetHashCode(T obj);
}
Желательно, чтобы во вспомогательном типе для проверки равенства были реализованы обе версии интерфейса IEqualityComparer. Существует абстрактный класс System.Collections.Generic.EqualityComparer<T>, реализующий интерфейсы IEqualityComparer<T> и IEqualityComparer и содержащий виртуальные методы Equals() и GetHashCode(). Можно наследовать от этого класса и заместить его виртуальные методы.
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class LastFirstComparer : EqualityComparer<Customer>
{
public override bool Equals(Customer x, Customer y)
{
return x.LastName == y.LastName &&
x.FirstName == y.FirstName;
}
public override int GetHashCode(Customer obj)
{
return (obj.LastName + ";" + obj.FirstName).GetHashCode();
}
}
Статическое свойство EqualityComparer<T>.Default возвращает экземпляр EqualityComparer<T> для типа T. Этот экземпляр использует метод Equals(T) либо метод Equals(object) в зависимости от того, реализует ли T интерфейс IEquatable<T>.