#c_sharp #net
Есть ли функция на подобие 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("ошибка"); } Есть ли готовые реализации нужной мне функции? Если нету, то как можно улучшить мою функцию?
Ответы
Ответ 1
Сама по себе идея плоха. Вы не можете вслепую, основываясь на лишь значениях полей, установить равенство объектов. Семантика равенства намного сложнее. Для начала, объекты, в отличие от структур, обладают самостоятельным смыслом, который не есть просто суммарный смысл их полей. Чтобы прояснить эту загадочную фразу, вот вам пример. Если у нас есть два человека, которых зовут Игорь Сергеев, мы ведь не можем на основании лишь этих данных утверждать, что это один и тот же человек? Точно так же и объекты с одинаковыми полями не обязательно равны: два экземпляра класса Car, одного производителя и года выпуска, с одним типом мотора и одинаково оснащённые — это не один и тот же автомобиль. Затем, объекты могут оказаться равными даже если у них не одинаковые значения полей. Возьмём к примеру объект «рациональная дробь». Дроби 3/7 и 6/14 равны, хотя ни их числители, ни знаменатели не равны. Затем, объект имеет право держать свои данные в самых неожиданных местах. Например, в WPF объекты держат свои многочисленные DependencyProperty не внутри объекта, а во внешнем словаре. А какой же выход? Очень простой. Если объект хочет уметь сравниваться с другими объектами, он должен не лениться и реализовать интерфейс IEquatable. Только он сам знает, как сравнивать себя с остальными. Кстати, все числовые типы именно так и делают, так что делать огромный switch по их типам вовсе не обязательно. А вместо универсальной функции сравнения возьмите EqualityComparer .Default. Она делает именно то, что надо: запрашивает IEquatable , а если его нет, использует виртуальный Equals. В C# объекты, которые есть не что иное, как значение своих полей (то есть, равенство полей означает равенство объектов), обычно оформляются в виде структур, а не классов. Операция сравнения для структур делает в точности то, что вы пытаетесь реализовать: сравнивает все поля. При этом если все поля рекурсивно вниз являются полями-структурами, то операция сравнения просто сравнивает объекты побитно, что, конечно, гораздо эффективнее. Но если структура требует кастомной логики сравнения, она всё равно должна реализовать её самостоятельно, через IEquatable . Дополнительное чтение по теме: Чем отличаются оператор == и вызов метода object.Equals в C#? Ответ 2
Ваша функция выглядит странно для строго типизированного языка. Может стоит подумать над изменением логики программы? По поводу вашей функции. Вместо всех этих проверок над стандартными структурами: if (type == typeof(ushort)) { return (ushort)value == (ushort)value2; } Вы можете просто написать вначале: if (value.Equals(value2)) return true; Потому что эти структуры переопределяют метод Equals. Например: object x = (short)-1; object y = (short)-1; Console.WriteLine(x.Equals(y)); Покажет true. Рекурсивная проверка полей/свойств классов упадет с исключением, если будут циклические ссылки. Например для такого класса: class Test { public Test Field; } var test = new Test(); test.Field = test;
Комментариев нет:
Отправить комментарий