【问题标题】:How to compare two distinctly different objects with similar properties如何比较具有相似属性的两个截然不同的对象
【发布时间】:2010-12-16 01:39:36
【问题描述】:

这一切都在 C# 中,使用 .NET 2.0。

我有两个对象列表。它们不是相关的对象,但它们确实具有某些东西 可以比较的共同点,例如基于 Guid 的唯一标识符。这两个列表需要 被另一个列表过滤,该列表仅包含可能匹配也可能不匹配的 Guid 前两个列表中包含的 ID。

我考虑过将每个对象列表转换为“对象”并按以下方式排序的想法 那个,但我不确定我是否能够访问 ID 属性,一旦它被转换,我 认为对两个列表进行排序的方法应该有点愚蠢,因为它知道什么是 要排序的列表是。

引入每个对象列表的最佳方法是什么,以便可以仅使用 ID 对列表进行排序?

【问题讨论】:

  • 排序还是过滤?按什么排序?
  • 实际过滤。 object.GuidId == list.GuidId 返回对象。

标签: c# design-patterns architecture oop


【解决方案1】:

您应该使每个不同的对象都实现一个通用接口。然后为该接口创建一个 IComparer 并在您的排序中使用它。

【讨论】:

  • 如果您可以实现一个通用接口(因为您不拥有一个或两个对象),您将希望使适配器类实现该通用接口。那你就是金子了。
  • 换句话说,您实际上是将每个对象转换为包含两者的共同元素的类(接口)类型,然后比较生成的(相同类型的)对象。使用接口可以避免显式转换操作。
  • 我认为有像 Jimmy's AutoMapper 这样的东西来为相关对象类型注册一个类似 IComparer 的类会很有趣。想法?
【解决方案2】:

好的,如果您有权修改您的原始类只是为了在其中添加接口,那么 Matthew 可以做到这一点。我在这里有点疯狂,并使用 2.0 匿名代表定义了一个完整的解决方案。 (我想我对 3.0 Lambda 上瘾了;否则,如果我还在使用 2005,我可能会在 foreach 循环中写出来)。

基本上,创建一个具有通用属性的接口。让你的两个类实现接口。创建一个转换为接口的通用列表,将值转换为新列表;删除任何不匹配的项目。

//Program Output: 
List1:
206aa77c-8259-428b-a4a0-0e005d8b016c
64f71cc9-596d-4cb8-9eb3-35da3b96f583

List2:
10382452-a7fe-4307-ae4c-41580dc69146
97f3f3f6-6e64-4109-9737-cb72280bc112
64f71cc9-596d-4cb8-9eb3-35da3b96f583

Matches:
64f71cc9-596d-4cb8-9eb3-35da3b96f583
Press any key to continue . . .


using System;
using System.Collections.Generic;
using System.Text;

namespace ConsoleApplication8
{
    class Program
    {
        static void Main(string[] args)
        {
            //test initialization
            List<ClassTypeA> list1 = new List<ClassTypeA>();
            List<ClassTypeB> list2 = new List<ClassTypeB>();

            ClassTypeA citem = new ClassTypeA();
            ClassTypeB citem2 = new ClassTypeB();
            citem2.ID = citem.ID;

            list1.Add(new ClassTypeA());
            list1.Add(citem);
            list2.Add(new ClassTypeB());
            list2.Add(new ClassTypeB());
            list2.Add(citem2);


            //new common list. 
            List<ICommonTypeMakeUpYourOwnName> common_list = 
                        new List<ICommonTypeMakeUpYourOwnName>();

            //in english,  give me everything in list 1 
            //and cast it to the interface
            common_list.AddRange(
              list1.ConvertAll<ICommonTypeMakeUpYourOwnName>(delegate(
                  ClassTypeA x) { return (ICommonTypeMakeUpYourOwnName)x; }));

            //in english, give me all the items in the 
            //common list that don't exist in list2 and remove them. 
            common_list.RemoveAll(delegate(ICommonTypeMakeUpYourOwnName x) 
               { return list2.Find(delegate(ClassTypeB y) 
                      {return y.ID == x.ID;}) == null; });

            //show list1 
            Console.WriteLine("List1:");
            foreach (ClassTypeA item in list1)
            {
                Console.WriteLine(item.ID);
            }
            //show list2
            Console.WriteLine("\nList2:");
            foreach (ClassTypeB item in list2)
            {
                Console.WriteLine(item.ID);
            }

            //show the common items
            Console.WriteLine("\nMatches:");
            foreach (ICommonTypeMakeUpYourOwnName item in common_list)
            {
                Console.WriteLine(item.ID);
            }
        }

    }

    interface ICommonTypeMakeUpYourOwnName
    {
        Guid ID { get; set; }
    }

    class ClassTypeA : ICommonTypeMakeUpYourOwnName
    {
        Guid _ID;
        public Guid ID {get { return _ID; } set { _ID = value;}}
        int _Stuff1;
        public int Stuff1 {get { return _Stuff1; } set { _Stuff1 = value;}}
        string _Stuff2;
        public string Stuff2 {get { return _Stuff2; } set { _Stuff2 = value;}}

        public ClassTypeA()
        {
            this.ID = Guid.NewGuid();
        }
    }

    class ClassTypeB : ICommonTypeMakeUpYourOwnName
    {
        Guid _ID;
        public Guid ID {get { return _ID; } set { _ID = value;}}
        int _Stuff3;
        public int Stuff3 {get { return _Stuff3; } set { _Stuff3 = value;}}
        string _Stuff4;
        public string Stuff4 {get { return _Stuff4; } set { _Stuff4 = value;}}

        public ClassTypeB()
        {
            this.ID = Guid.NewGuid();
        }

    }
}

【讨论】:

  • 仅供参考... OP说2.0;我假设 2005 VS,所以没有 3.0 VS 2008 快捷方式。
  • 这很好用。太糟糕了,我不能给出两个被接受的答案。
【解决方案3】:

仅使用 .NET 2.0 方法:

class Foo
{
    public Guid Guid { get; }
}

List<Foo> GetFooSubset(List<Foo> foos, List<Guid> guids)
{
    return foos.FindAll(foo => guids.Contains(foo.Guid));
}

如果您的类没有实现通用接口,则必须为每种类型分别实现GetFooSubset

【讨论】:

  • .NET 2.0 不支持 lambda 表达式,也没有 System.Linq 添加的扩展方法。您的示例至少需要 .NET 3.0。
  • @Karim:FindAll 不是 LINQ 添加的扩展方法,而是 List&lt;T&gt; 类提供的常规方法,该类自 2.0 版起成为 .NET 框架的一部分。 Lambda 表达式需要 C# 3.0 编译器,但不一定需要 .NET 3.0。
【解决方案4】:

我不确定我是否完全理解您想要的内容,但您可以使用 linq 从列表中选择匹配项并对它们进行排序。这是一个简单的示例,其中一个列表中的值在另一个列表中过滤并排序。

        List<int> itemList = new List<int>() { 9,6,3,4,5,2,7,8,1 };
        List<int> filterList = new List<int>() { 2, 6, 9 };

        IEnumerable<int> filtered = itemList.SelectMany(item => filterList.Where(filter => filter == item)).OrderBy(p => p);

【讨论】:

    【解决方案5】:

    我还没有机会使用 AutoMapper,但是根据您的描述,您希望 check it out。来自 Jimmy Bogard 的帖子:

    AutoMapper 约定

    由于 AutoMapper 变平,它会 寻找:

    匹配的属性名称

    嵌套属性名称(Product.Name 映射到 ProductName,假设一个 PascalCase 命名约定)

    以“Get”开头的方法, 所以 GetTotal() 映射到 Total

    任何现有的类型映射已经 已配置

    基本上,如果您删除了所有 “dots”和“Gets”,AutoMapper 将 匹配属性名称。现在, AutoMapper 在不匹配时不会失败 类型,但出于其他一些原因。

    【讨论】:

      【解决方案6】:

      但是,我不完全确定您想要什么作为最终结果....

      如果您要比较两种不同类型的属性,您可以将属性名称和相应的值投影到两个字典中。并利用这些信息对属性值进行某种排序/差异。

              Guid newGuid = Guid.NewGuid();
              var classA = new ClassA{Id = newGuid};
              var classB = new ClassB{Id = newGuid};
      
              PropertyInfo[] classAProperties = classA.GetType().GetProperties();
      
              Dictionary<string, object> classAPropertyValue = classAProperties.ToDictionary(pName => pName.Name,
                                                                                      pValue =>
                                                                                      pValue.GetValue(classA, null));
      
              PropertyInfo[] classBProperties = classB.GetType().GetProperties();
              Dictionary<string, object> classBPropetyValue = classBProperties.ToDictionary(pName => pName.Name,
                                                                                      pValue =>
                                                                                      pValue.GetValue(classB, null));
      
      
      internal class ClassB
      {
          public Guid Id { get; set; }
      }
      
      internal class ClassA
      {
          public Guid Id { get; set; }
      }
      
      classAPropertyValue
      Count = 1
          [0]: {[Id, d0093d33-a59b-4537-bde9-67db324cf7f6]}
      
      classBPropetyValue
      Count = 1
          [0]: {[Id, d0093d33-a59b-4537-bde9-67db324cf7f6]}
      

      【讨论】:

        【解决方案7】:

        Thist 应该能得到你想要的——但你可能会更好地使用 linq

        class T1
        {
            public T1(Guid g, string n) { Guid = g; MyName = n; }
            public Guid Guid { get; set; }
            public string MyName { get; set; }
        }
        class T2
        {
            public T2(Guid g, string n) { ID = g; Name = n; }
            public Guid ID { get; set; }
            public string Name { get; set; }
        }
        class Test
        {
            public void Run()
            {
                Guid G1 = Guid.NewGuid();
                Guid G2 = Guid.NewGuid();
                Guid G3 = Guid.NewGuid();
                List<T1> t1s = new List<T1>() {
                    new T1(G1, "one"),
                    new T1(G2, "two"), 
                    new T1(G3, "three") 
                };
                List<Guid> filter = new List<Guid>() { G2, G3};
        
                List<T1> filteredValues1 = t1s.FindAll(delegate(T1 item)
                {
                    return filter.Contains(item.Guid);
                });
        
                List<T1> filteredValues2 = t1s.FindAll(o1 => filter.Contains(o1.Guid));
            }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-08-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-05-08
          • 2018-08-28
          • 2016-09-15
          • 2022-01-18
          相关资源
          最近更新 更多