Страницы

Поиск по вопросам

четверг, 18 октября 2018 г.

Универсальный метод для проверки объектов на равенство C#

Есть ли функция на подобие Equals, которая вместо ссылок сравнивает все значения объекта, а также значения вложенных объектов? Я не нашел ничего нужного и попытался реализовать эту функцию самостоятельно, получилось так:
public static class EqualsUtil { public static bool Equals(object value, object value2) { if (value == value2) return true;
if (value == null || value2 == null) return false;
Type type = value.GetType();
if (type == typeof(int)) { return (int)value == (int)value2; }
if (type == typeof(string)) { return String.CompareOrdinal((string) value, (string) value2) == 0; }
if (type == typeof(byte)) { return (byte)value == (byte)value2; }
if (type == typeof(byte?)) { return (byte?)value == (byte?)value2; }
if (type == typeof(sbyte)) { return (sbyte)value == (sbyte)value2; }
if (type == typeof(sbyte?)) { return (sbyte?)value == (sbyte?)value2; }
if (type == typeof(uint)) { return (uint)value == (uint)value2; }
if (type == typeof(uint?)) { return (uint?)value == (uint?)value2; }
if (type == typeof(short)) { return (short)value == (short)value2; }
if (type == typeof(short?)) { return (short?)value == (short?)value2; }
if (type == typeof(ushort)) { return (ushort)value == (ushort)value2; }
if (type == typeof(ushort?)) { return (ushort?)value == (ushort?)value2; }
if (type == typeof(long)) { return (long)value == (long)value2; }
if (type == typeof(long?)) { return (long?)value == (long?)value2; }
if (type == typeof(ulong)) { return (ulong)value == (ulong)value2; }
if (type == typeof(ulong?)) { return (ulong?)value == (ulong?)value2; }
if (type == typeof(float)) { return (float)value == (float)value2; }
if (type == typeof(float?)) { return (float?)value == (float?)value2; }
if (type == typeof(double)) { return (double)value == (double)value2; }
if (type == typeof(double?)) { return (double?)value == (double?)value2; }
if (type == typeof(char)) { return (char)value == (char)value2; }
if (type == typeof(char?)) { return (char?)value == (char?)value2; }
if (type == typeof(bool)) { return (bool)value == (bool)value2; }
if (type == typeof(bool?)) { return (bool?)value == (bool?)value2; }
if (type == typeof(decimal)) { return (decimal)value == (decimal)value2; }
if (type == typeof(decimal?)) { return (decimal?)value == (decimal?)value2; }
if (type.IsClass) { var filds = type.GetFields(); foreach (var field in filds) { var v1 = field.GetValue(value); var v2 = field.GetValue(value2); if (!EqualsUtil.Equals(v1, v2)) { return false; } } var properties = type.GetProperties(); foreach (var property in properties) { var v1 = property.GetValue(value); var v2 = property.GetValue(value2); if (!EqualsUtil.Equals(v1, v2)) { return false; } } return true; }
throw new Exception("ошибка"); }
Есть ли готовые реализации нужной мне функции? Если нету, то как можно улучшить мою функцию?


Ответ

Сама по себе идея плоха.
Вы не можете вслепую, основываясь на лишь значениях полей, установить равенство объектов. Семантика равенства намного сложнее.
Для начала, объекты, в отличие от структур, обладают самостоятельным смыслом, который не есть просто суммарный смысл их полей. Чтобы прояснить эту загадочную фразу, вот вам пример. Если у нас есть два человека, которых зовут Игорь Сергеев, мы ведь не можем на основании лишь этих данных утверждать, что это один и тот же человек? Точно так же и объекты с одинаковыми полями не обязательно равны: два экземпляра класса Car, одного производителя и года выпуска, с одним типом мотора и одинаково оснащённые — это не один и тот же автомобиль.
Затем, объекты могут оказаться равными даже если у них не одинаковые значения полей. Возьмём к примеру объект «рациональная дробь». Дроби 3/7 и 6/14 равны, хотя ни их числители, ни знаменатели не равны.
Затем, объект имеет право держать свои данные в самых неожиданных местах. Например, в WPF объекты держат свои многочисленные DependencyProperty не внутри объекта, а во внешнем словаре.
А какой же выход? Очень простой. Если объект хочет уметь сравниваться с другими объектами, он должен на лениться и реализовать интерфейс IEquatable. Только он сам знает, как сравнивать себя с остальными. Кстати, все числовые типы именно так и делают, так что делать огромный switch по их типам вовсе не обязательно.
А вместо универсальной функции сравнения возьмите EqualityComparer.Default. Она делает именно то, что надо: запрашивает IEquatable, а если его нет, использует виртуальный Equals

В C# объекты, которые есть не что иное, как значение своих полей (то есть, равенство полей означает равенство объектов), обычно оформляются в виде структур, а не классов. Операция сравнения для структур делает в точности то, что вы пытаетесь реализовать: сравнивает все поля. При этом если все поля рекурсивно вниз являются полями-структурами, то операция сравнения просто сравнивает объекты побитно, что, конечно, гораздо эффективнее. Но если структура требует кастомной логики сравнения, она всё равно должна реализовать её самостоятельно, через IEquatable

Дополнительное чтение по теме: Чем отличаются оператор == и вызов метода object.Equals в C#?

Комментариев нет:

Отправить комментарий