В этой статье я расскажу о довольно простой вещи в C#: Сравните два объекта. Это задача, которую все считают легкой; Постепенно буду поднимать вопрос от простого к сложному. Обходной путь также будет идти от простого к сложному, а затем обратно к простому. Если вы потратите время на то, чтобы прочитать эту статью от начала до конца, вы многое поймете, а технические возможности прилично повысятся.
Уровень 1: Простой класс
Начнем задачу с простого класса. Этот класс имеет три свойства, когда мы вызываем функцию Equals, C# сравнивает только ссылку, поэтому результат False
public
class
Student { public
string
FirstName { get; set; } public
string
LastName { get; set; } public
int
Age { get; set; } }
var
student1 = new
Student {
FirstName = "Pham", LastName = "Hoang", Age = 15, };
var
student2 = new
Student { FirstName = "Pham", LastName = "Hoang", Age = 15, };
student1.Equals(student2); //False
Чтобы справиться с этим, нам нужно переопределить функцию Equals, а это не сложно, все узнают в школе.
public
override
bool
Equals(object
obj) {
if
(obj == null
|| GetType() != obj.GetType()) return
false;
var
student = (Student) obj;
return
FirstName.Equals(student.FirstName) && LastName.Equals(student.LastName) && Age == student.Age && BirthDate.Equals(student.BirthDate); }
Уровень 2: Используйте метод расширения
У функции Equals все еще есть недостаток: student1 не может быть нулевым. Мы можем решить эту проблему, используя метод расширения следующим образом. Теперь мы можем вызвать student1. Равно (student2), несмотря на то, что student1 имеет значение null:
public static bool DeepEquals (this Student obj, Student another) { // If null or the same, return true if (ReferenceEquals (obj, another)) return true; // If one of them is null, return false if ((obj == null) || (another == null)) return false; return obj.FirstName.Equals (another.FirstName) && obj.LastName.Equals (another.LastName) && obj.Age == another.Age && obj.BirthDate.Equals (another.BirthDate); }
Уровень 3: Напишите метод для сравнения двух объектов в целом.
Представьте, что в вашем приложении есть несколько десятков классов, каждый из которых имеет несколько десятков свойств. Что вы будете делать? Рукописная функция Equals для каждого класса? Используйте Refection на C#, чтобы написать функцию сравнения объектов, которая может использоваться любым объектом.
public static bool DeepEquals (this object obj, object another) { if (ReferenceEquals (obj, another)) return true; if ((obj == null) || (another == null)) return false; // Comparing class of 2 objects, if different, then fail if (obj.GetType ()! = another.GetType ()) return false; var result = true; // Get all properties of obj // then compare the value of each property foreach (var property in obj.GetType (). GetProperties ()) { var objValue = property.GetValue (obj); var anotherValue = property.GetValue (another); if (! objValue.Equals (anotherValue)) result = false; } return result; }
Эта функция работает очень хорошо. Вы задаетесь вопросом: Вау, это легко писать? Пока нет, пожалуйста, продолжайте ниже, нас ждут более «страшные» вещи.
Уровень 4: объект содержит объект или структуру, например DateTime.
На уровне выше мы пишем функцию для сравнения каждого поля. Но предположим, что в классе Student есть DateTime или другой класс?
public class Student { public string FirstName {get; set; } public string LastName {get; set; } public DateTime BirthDate {get; set; } public Teacher Teacher {get; set; } } public class Teacher { public string Name {get; set; } public string Subject {get; set; } } var student1 = new Student { FirstName = "Pham", LastName = "Hoang", BirthDate = new DateTime (1992, 12, 5), Teacher = new Teacher {Name = "Le Minh", Subject = "Math"} }; var student2 = new Student { FirstName = "Pham", LastName = "Hoang", BirthDate = new DateTime (1992, 12, 5), Teacher = new Teacher {Name = "Le Minh", Subject = "Math"} };
Задумайтесь на секунду. Чтобы решить эту проблему, мы также будем сравнивать каждое поле, но если поле является объектом, то мы будем сравнивать его с написанной функцией DeepEquals. Только базовый рекурсивный алгоритм.
public static bool DeepEquals (this object obj, object another) { if (ReferenceEquals (obj, another)) return true; if ((obj == null) || (another == null)) return false; if (obj.GetType ()! = another.GetType ()) return false; // If the property is not a class, just int, double, DateTime, etc. v // Call regular equal function if (! obj.GetType (). IsClass) return obj.Equals (another); var result = true; foreach (var property in obj.GetType (). GetProperties ()) { var objValue = property.GetValue (obj); var anotherValue = property.GetValue (another); // Continue recursion if (! objValue.DeepEquals (anotherValue)) result = false; } return result; }
Ничего себе, дело сделано, все проблемы решены, вы себя хвалите. О, как насчет случая не объекта, а List, как насчет этой натяжки.
Уровень 5: Сравните 2 списка
К счастью, мы можем написать метод расширения для списка следующим образом (слово ‹T› является общим, вы можете прочитать эту статью для ознакомления).
public static bool DeepEquals <T> (this IEnumerable <T> obj, IEnumerable <T> another) { if (ReferenceEquals (obj, another)) return true; if ((obj == null) || (another == null)) return false; bool result = true; // Browse each element in 2 given list using (IEnumerator <T> enumerator1 = obj.GetEnumerator ()) using (IEnumerator <T> enumerator2 = another.GetEnumerator ()) { while (true) { bool hasNext1 = enumerator1.MoveNext (); bool hasNext2 = enumerator2.MoveNext (); // If there is 1 list, or 2 different elements, exit the loop if (hasNext1! = hasNext2 ||! enumerator1.Current.DeepEquals (enumerator2.Current)) { result = false; break; } // Stop the loop when 2 lists are all if (! hasNext1) break; } } return result; }
Вау, я временно закончил. Наверное, больше ничего, да?
var list1 = new List <Student> {student1, student2}; var list2 = new List <Student> {student1, student2}; list1 == list2; // True
Уровень 6: Куча других вещей
Вы вдруг вспоминаете, что в C# есть бессчетное количество вещей, похожих на List, таких как Dictionary, HashSet, может быть, чтобы писать в них все. Есть еще несколько тривиальных случаев, например, класс Student будет содержать список учителей; метод, который мы пишем, не может работать.
var teacherA = new Teacher {Name = "Le Minh", Subject = "Math"}; var teacherB = new Teacher {Name = "Tai Phu", Subject = "Physics"}; var student1 = new Student { FirstName = "Pham", LastName = "Hoang", Age = 15, BirthDate = new DateTime (1992, 12, 5), Teacher = new List <Teacher> {teacherA, teacherB} }; var student2 = new Student { FirstName = "Pham", LastName = "Hoang", Age = 15, BirthDate = new DateTime (1992, 12, 5), Teacher = new List <Teacher> {teacherA, teacherB} };
На данный момент я тоже сдался, дорога впереди довольно сложная и трудная: ‘(Вы можете выбрать один из двух следующих вариантов:
- Для слишком сложных классов напишите функцию Equals самостоятельно, не слишком.
- Следуйте универсальной функции до конца, продолжая читать эту статью.
Конечный уровень: JSON
Решение проблемы проще, чем вы думаете. Давайте сериализуем эти два объекта как строку JSON, сравним две сгенерированные строки. (К счастью, сериализация JSON решила 99% сложной проблемы, связанной с типами данных: D).
Этапы, которые необходимо выполнить:
- Добавьте Reference Newtonsoft.JSON в соответствии с инструкциями, результат, как показано на рисунке 3, в порядке.
2. Напишите простую компактную функцию сравнения:
public static bool JSONEquals (this object obj, object another) { if (ReferenceEquals (obj, another)) return true; if ((obj == null) || (another == null)) return false; if (obj.GetType ()! = another.GetType ()) return false; var objJson = JsonConvert.SerializeObject (obj); var anotherJson = JsonConvert.SerializeObject (another); return objJson == anotherJson; }
Есть еще несколько библиотек, которые вы можете найти в Google по ключевым словам: Deep Compare C#.
Так как статья довольно длинная, слегка техническая, я постарался сделать ее более привлекательной. Поздравляю, если вы дочитали до конца. Награда для вас здесь терпеливая: первые пять комментаторов в этой статье имеют право попросить меня написать статью об одном аспекте C#, MVC или javascript, который вы хотите изучить. Удачи.