如何比较具有相似属性的两个截然不同的对象

时间:2009-11-12 20:56:01

标签: c# design-patterns architecture oop

这完全在C#中,使用.NET 2.0。

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

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

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

7 个答案:

答案 0 :(得分:15)

您应该让每个不同的对象实现一个通用接口。然后创建IComparer< T>对于该界面并在您的排序中使用它。

答案 1 :(得分:2)

好的,如果你有权修改你的原始类只是为了在那里添加界面,马修就把它发现了。我在这里有点疯狂,并使用2.0匿名代表定义了一个完整的解决方案。 (我想我已经沉迷于3.0 Lambda了;否则,如果我还在使用2005,我可能会在foreach循环中写出来。)

基本上,使用公共属性创建一个接口。让yoru两个类实现接口。创建一个作为接口的通用列表,将值转换并将其转换为新列表;删除任何不匹配的项目。

//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();
        }

    }
}

答案 2 :(得分:1)

仅使用.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

答案 3 :(得分:0)

我不确定我是否完全理解你想要的东西,但是你可以使用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);

答案 4 :(得分:0)

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

  

AutoMapper约定

     

由于AutoMapper会变平,它会   寻找:

     

匹配属性名称

     

嵌套属性名称(Product.Name   通过假设a映射到ProductName   PascalCase命名约定)

     

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

     

已有任何现有的类型地图   构造

     

基本上,如果你删除了所有的   “点”和“获取”,AutoMapper将   匹配属性名称。马上,   AutoMapper在不匹配时不会失败   类型,但由于其他一些原因。

答案 5 :(得分:0)

然而,我并不完全确定你想要的最终结果......

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

        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]}

答案 6 :(得分:0)

本质上应该可以满足您的需求 - 但您最好使用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));
    }
}