RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 563071
Accepted
4per
4per
Asked:2020-09-05 22:05:42 +0000 UTC2020-09-05 22:05:42 +0000 UTC 2020-09-05 22:05:42 +0000 UTC

.Net 中的参考类比较方法

  • 772

以前,我认为有可能并且有必要为您的类重新定义 Equals。但是我遇到了其他信息,覆盖可能会导致某些集合出现问题。

  1. 你什么时候应该重写object.Equals()你的课程?

  2. 真的,如果你想根据本质比较类的两个对象是否等价,你需要创建一个单独的方法吗?

  3. 第三方课程呢?原来他们没有提供实体等价性的比较手段?

我对实体等价性的理解是什么。假设有一堂课

public class Country
{ 
     public Country (string name)
     {
          this.name = name;
     }
     private readonly string name;
     public string Name { get {return name;}}
}

public void Main()
{
      var c1 = new Country("Россия");
      var c2 = new Country("Россия");
}

所以 c1 和 c2 对我来说是等价的,因为 我的世界里不能有两个同名的国家。恰好我们创建了两个实例,但它们本质上是相同的..

c#
  • 3 3 个回答
  • 10 Views

3 个回答

  • Voted
  1. Best Answer
    andreycha
    2020-09-05T22:37:26Z2020-09-05T22:37:26Z

    如您所知,默认情况下 .NET通过引用比较引用类型的对象,以及值类型的对象 -逐位(按值读取)。

    这在您的示例中导致了什么?事实上,c1他们c2被认为是不平等的。随着 t.z. 业务逻辑,您正确地注意到它们是相等的,但是,环境对业务逻辑一无所知。

    因此得出结论:

    1. Equals()在需要根据某些特定规则将对象视为相等的情况下,必须重写该方法。特别是,当您将该类型的对象用作字典键、哈希集元素或某些集合的元素并调用Contains(). 还值得注意的是,与 k 配对时Equals(),您需要覆盖GetHashCode().

    2. 是的需要。因为相同类型的两个对象相等的规则,粗略地说,是业务规则,适用于您的应用程序。运行时对它们一无所知,但作为开发人员的您知道它们。

    3. 对于第三方类实例的自定义比较,使用接口IEqualityComparer<T>及其实现。BCL 包括一些开箱即用的实现(例如,用于不区分大小写的字符串比较)。在大多数情况下,您需要创建自己的比较器。

    • 13
  2. user177221
    2020-09-05T22:32:11Z2020-09-05T22:32:11Z
    1. object.Equals()如果您的类的实例有一个真实的方法来确定所有应用程序代码通用的等效性,则值得重新定义。重写时object.Equals(),一定要重写方法,并以这样的方式object.GetHashCode()为等效对象GetHashCode()返回相同的值。

    2. 不,覆盖object.Equals()不是唯一可用的方法。如果您需要在一个特定案例中指定等效性的定义,而不是在整个应用程序中,那么您可以通过IEqualityComparer<T>. 几乎所有标准集合都允许您使用IEqualityComparer<T>- 作为特定查找方法的参数,或作为集合构造函数的参数。

    3. 对于第三方课程 - 请参阅 2。

    • 9
  3. Kirill
    2022-08-06T21:51:59Z2022-08-06T21:51:59Z
    using System;
    using System.Collections;
    using System.Reflection;
    using System.Collections.Generic;
    using System.Linq;
    
    namespace TestsComponents
    {
        /// <summary>
        /// Класс для тестирования методом рефлексии.
        /// </summary>
        public static class ReflectionComparer
        {
            /// <summary>
            ///  record Result
            /// </summary>
            /// <param name="Boolean">Результат операции, где "false" - экземпляры отличаются и "true" - экземпляры классов совпали</param>
            /// <param name="Message">Информационное сообщение о подробной информации</param>
            public record Result(Boolean Boolean, String Message);
            /// <summary>
            /// record MemberInfoAndMeaning
            /// </summary>
            /// <param name="Member">Краткая информация о поле или свойстве класса в виде: "Тип - Имя"</param>
            /// <param name="Meaning">Значение поля или свойства</param>
            public record MemberInfoAndMeaning(String Member, Object Meaning);
            /// <summary>
            /// Метод тестирования на основе рефлексии. Принимает два объекта, а так же коллекции из объектов.
            /// </summary>
            /// <param name="obj1">Object который принимает первый элемент для сравнения.</param>
            /// <param name="obj2">Object который принимает второй элемент для сравнения.</param>
            /// <param name="virtual_or_not">Включить поиск по элементам с модификатором "виртуальный" по умолчанию стоит  false.</param>
            /// <param name="to_lower">Отвечает за сравнивание названий полей или свойств с приоритетом к реестру, по умолчанию стоит false.</param>
            /// <returns>Возвращает record "Result", где "Boolean" результат операции, и "Message" сообщение о замечаниях (С коллекциями при несовпадении возвращает "Коллекции классов совпали!")</returns>
            public static Result ReflectionEquals(Object obj1, Object obj2, Boolean virtual_or_not = false, Boolean to_lower = false)
            {
                List<String> remarks = new();
    
                if (obj1.GetType().IsClass && obj2.GetType().IsClass || obj1 is IEnumerable && obj2 is IEnumerable)
                {
                    Type type1;
                    Type type2;
    
                    if (obj1 is ICollection enumerable_obj1 && obj2 is ICollection enumerable_obj2)
                    {
                        foreach (Object variable_obj1 in enumerable_obj1)
                        {
                            //Экземпляр
                            obj1 = variable_obj1;
                            //Типы
                            type1 = variable_obj1.GetType();
                            Int32 count = 1;
                            foreach (Object variable_obj2 in enumerable_obj2)
                            {
                                //Экземпляр
                                obj2 = variable_obj2;
                                //Типы
                                type2 = variable_obj2.GetType();
                                (Boolean boolean, _) = GettingTypes(
                                    obj1: obj1,
                                    obj2: obj2,
                                    type1: type1,
                                    type2: type2,
                                    remarks: remarks,
                                    virtual_or_not: virtual_or_not,
                                    to_lower: to_lower);
                                if (!boolean && enumerable_obj2.Count == count)
                                {
                                    return new Result(Boolean: false, Message: "Класс не был найден в коллекции!");
                                }
                                else if (!boolean && enumerable_obj2.Count != count)
                                {
                                    remarks.Clear();
                                }
                                else if (boolean)
                                {
                                    break;
                                }
                                count++;
                            }
                        }
    
                        return new Result(Boolean: true, Message: "Коллекции классов совпали!");
                    }
                    else
                    {
                        type1 = obj1.GetType();
                        type2 = obj2.GetType();
                        //ищу поле и свойство
                        return GettingTypes(
                            obj1: obj1,
                            obj2: obj2,
                            type1: type1,
                            type2: type2,
                            remarks: remarks,
                            virtual_or_not: virtual_or_not,
                            to_lower: to_lower);
                    }
                }
                //Простые типы
                else
                {
                    if (obj1.Equals(obj: obj2))
                    {
                        return new Result(Boolean: true, Message: "Значения равны!");
                    }
                    else
                    {
                        return new Result(Boolean: false, Message: $"Значение в obj1: {obj1}  и значение в obj2: {obj2}  не совпали!");
                    }
                }
            }
            /// <summary>
            /// Метод для получения имени элемента и значения.
            /// </summary>
            /// <param name="obj1">Экземпляр первого класса в котором ведем поиск.</param>
            /// <param name="obj2">Экземпляр второго класса в котором ведем поиск.</param>
            /// <param name="type1">Полученный тип первого экземпляра.</param>
            /// <param name="type2">Полученный тип второго экземпляра.</param>
            /// <param name="remarks">Список ошибок.</param>
            /// <param name="virtual_or_not">Включить поиск по элементам с модификатором "виртуальный" по умолчанию стоит  false.</param>
            /// <param name="to_lower">Отвечает за сравнивание названий полей или свойств с приоритетом к реестру, по умолчанию стоит false.</param>
            /// <returns>Возвращает record "Result", где "Boolean" результат операции, и "Message" сообщение о замечаниях (С коллекциями при несовпадении возвращает "Коллекции классов совпали!")</returns>
            private static Result GettingTypes(Object obj1, Object obj2, Type type1, Type type2, List<String> remarks, Boolean virtual_or_not, Boolean to_lower)
            {
                MemberTypes[] member_types = { MemberTypes.Field, MemberTypes.Property };
    
                List<MemberInfoAndMeaning> member_infos_type1 = new();
                List<MemberInfoAndMeaning> member_infos_type2 = new();
    
                foreach (MemberInfo member_info in type1.GetMembers().Where(predicate: m => member_types.Contains(value: m.MemberType)).ToList())
                {
                    Object meaning = null;
    
                    try
                    {
                        if (type1.GetProperty(name: member_info.Name)!.GetGetMethod().IsVirtual == virtual_or_not)
                        {
                            switch (member_info.MemberType)
                            {
                                //Свойства
                                case MemberTypes.Property:
                                    {
                                        meaning = type1.GetProperty(name: member_info.Name)?.GetValue(obj: obj1);
                                        break;
                                    }
                                //Поле
                                case MemberTypes.Field:
                                    {
                                        meaning = type1.GetField(name: member_info.Name)?.GetValue(obj: obj1);
                                        break;
                                    }
                            }
    
                            String member;
                            member = to_lower ? member_info.ToString().ToLower() : member_info.ToString();
                            member_infos_type1.Add(item: new MemberInfoAndMeaning(Member: member, Meaning: meaning));
                        }
                    }
                    catch (Exception)
                    {
                        // ignored
                    }
                }
    
                foreach (MemberInfo member_info in type2.GetMembers().Where(predicate: m => member_types.Contains(value: m.MemberType)).ToList())
                {
                    Object meaning = null;
    
                    try
                    {
                        if (type2.GetProperty(name: member_info.Name)!.GetGetMethod().IsVirtual == virtual_or_not)
                        {
                            switch (member_info.MemberType)
                            {
                                //Свойства
                                case MemberTypes.Property:
                                    {
                                        meaning = type2.GetProperty(name: member_info.Name)?.GetValue(obj: obj2);
                                        break;
                                    }
                                //Поле
                                case MemberTypes.Field:
                                    {
                                        meaning = type2.GetField(name: member_info.Name)?.GetValue(obj: obj2);
                                        break;
                                    }
                            }
    
                            String member;
                            member = to_lower ? member_info.ToString().ToLower() : member_info.ToString();
                            member_infos_type2.Add(item: new MemberInfoAndMeaning(Member: member, Meaning: meaning));
                        }
                    }
                    catch (Exception)
                    {
                        // ignored
                    }
    
                }
    
                if (member_infos_type1.Count >= member_infos_type2.Count)
                {
                    Examination(member_infos_type1: member_infos_type1, member_infos_type2: member_infos_type2, remarks: remarks, type1: type1, type2: type2);
                }
                else
                {
                    Examination(member_infos_type1: member_infos_type2, member_infos_type2: member_infos_type1, remarks: remarks, type1: type2, type2: type1);
                }
    
                if (remarks.Count > 0)
                {
                    String message = remarks.Aggregate(seed: String.Empty, func: (current, remark) => current + "  Замечание: " + remark);
                    return new Result(Boolean: false, Message: message);
                }
                else
                {
                    return new Result(Boolean: true, Message: "Все элементы и их значения были найдены!");
                }
            }
            /// <summary>
            /// Метод для сравнения имен элементов и их значений.
            /// </summary>
            /// <param name="member_infos_type1">Коллекция (имя - значение) экземпляры меняются местами от количества найденных полей и значений.</param>
            /// <param name="member_infos_type2">Коллекция (имя - значение) экземпляры меняются местами от количества найденных полей и значений.</param>
            /// <param name="remarks">Список ошибок для заполнения.</param>
            /// <param name="type1">Тип экземпляра класса</param>
            /// <param name="type2">Тип экземпляра класса</param>
            private static void Examination(List<MemberInfoAndMeaning> member_infos_type1, List<MemberInfoAndMeaning> member_infos_type2, List<String> remarks, Type type1, Type type2)
            {
                foreach ((String member, Object meaning) in member_infos_type1)
                {
                    for (Int32 count_index = 0; count_index < member_infos_type1.Count; count_index++)
                    {
                        //проверяем Тип - Имя свойства или поля
                        if (member == member_infos_type2[index: count_index].Member)
                        {
                            if (meaning != null)
                            {
                                //проверяем object по фактическим значениям
                                if (meaning.Equals(obj: member_infos_type2[index: count_index].Meaning))
                                {
                                    break;
                                }
                                else
                                {
                                    remarks.Add(item: $"Элемент: {member} был найден! Но значение не совпало! Значения: {meaning} в {type1.Name} и {member_infos_type2[index: count_index].Meaning ?? "NULL"} в {type2.Name}");
                                    break;
                                }
                            }
                            else
                            {
                                if (member_infos_type2[index: count_index].Meaning != null)
                                {
                                    remarks.Add($"Элемент: {member} был найден! Но значение не совпало! Значения: {"NULL"} в {type1.Name} и {member_infos_type2[index: count_index].Meaning} в {type2.Name}");
                                    break;
                                }
                                else
                                {
                                    break;
                                }
                            }
                        }
                        else
                        {
                            if (member_infos_type2.Count == count_index + 1)
                            {
                                remarks.Add(item: $"Элемент: {member} с значением: {meaning} не был найден в экземпляре: {type2.Name}");
                                break;
                            }
                        }
                    }
                }
            }
        }
    }
    
    • 0

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    如何停止编写糟糕的代码?

    • 3 个回答
  • Marko Smith

    onCreateView 方法重构

    • 1 个回答
  • Marko Smith

    通用还是非通用

    • 2 个回答
  • Marko Smith

    如何访问 jQuery 中的列

    • 1 个回答
  • Marko Smith

    *.tga 文件的组重命名(3620 个)

    • 1 个回答
  • Marko Smith

    内存分配列表C#

    • 1 个回答
  • Marko Smith

    常规赛适度贪婪

    • 1 个回答
  • Marko Smith

    如何制作自己的自动完成/自动更正?

    • 1 个回答
  • Marko Smith

    选择斐波那契数列

    • 2 个回答
  • Marko Smith

    所有 API 版本中的通用权限代码

    • 2 个回答
  • Martin Hope
    jfs *(星号)和 ** 双星号在 Python 中是什么意思? 2020-11-23 05:07:40 +0000 UTC
  • Martin Hope
    hwak 哪个孩子调用了父母的静态方法?还是不可能完成的任务? 2020-11-18 16:30:55 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    user207618 Codegolf——组合选择算法的实现 2020-10-23 18:46:29 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    Arch ArrayList 与 LinkedList 的区别? 2020-09-20 02:42:49 +0000 UTC
  • Martin Hope
    iluxa1810 哪个更正确使用:if () 或 try-catch? 2020-08-23 18:56:13 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5