#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;
Комментариев нет:
Отправить комментарий