2009-11-12 4 views
5

To wszystko w języku C#, przy użyciu .NET 2.0.Jak porównać dwa wyraźnie różne obiekty o podobnych właściwościach?

Mam dwie listy obiektów. Nie są obiektami pokrewnymi, ale mają pewne wspólne cechy, takie jak unikalny identyfikator oparty na Guid. Te dwie listy muszą być filtrowane przez inną listę, która zawiera Guida, która może lub nie może się równać z identyfikatorami zawartymi na pierwszych dwóch listach.

Zastanowiłem się nad pomysłem rzucania każdej listy obiektów na "obiekt" i sortowanie według , ale nie jestem pewien, czy będę w stanie uzyskać dostęp do właściwości ID po jej rzuceniu, a ja " m myśląc, że metoda sortowania dwóch list powinna być nieco głupia, wiedząc, co to jest sortowana lista .

Jaki byłby najlepszy sposób umieszczenia każdej listy obiektów, aby można było posortować ją na listę tylko z identyfikatorami?

+0

Sortowanie lub filtrowanie? Sortowanie według czego? – dtb

+0

Właściwe filtrowanie. object.GuidId == list.GuidId zwraca obiekt. – Chris

Odpowiedz

15

Powinieneś sprawić, aby każdy z twoich różnych obiektów implementował wspólny interfejs. Następnie stwórz IComparer < T> dla tego interfejsu i użyj go w swoim sortowaniu.

+3

Jeśli możesz zaimplementować wspólny interfejs (ponieważ nie posiadasz jednego lub obu obiektów), będziesz chciał utworzyć klasy adapterów, które wykonają wspólny interfejs. Wtedy jesteś złoty. – plinth

+0

Innymi słowy, zasadniczo rzucasz każdy obiekt na typ klasy (interfejsu), który zawiera wspólne elementy obu, a następnie porównuje wynikowe (takie same typy) obiektów. Korzystanie z interfejsu pozwala uniknąć jawnych operacji rzutowania. –

+0

Myślę, że byłoby interesujące mieć coś takiego jak AutoMapper Jimmy'ego, aby zarejestrować klasę podobną do IComparer dla powiązanych typów obiektów. Myśli? –

0

Nie jestem pewien, czy w pełni rozumiem, czego chcesz, ale możesz użyć linq do wybrania pasujących elementów z list oraz ich sortowania. Oto prosty przykład, w którym wartości z jednej listy są filtrowane na innym i sortowane.

 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); 
0

Nie miałem okazję używać jeszcze AutoMapper, ale z tego co opisujesz chcesz check it out. Jimmy Bogard poście:

AutoMapper konwencji

Od AutoMapper spłaszcza, to wygląd:

nazw właściwości Matching

zagnieżdżone nazwy właściwości (Product.Name mapy do NazwaProduktu, przy założeniu konwencji nazewnictwa PascalCase )

Metody rozpoczynające się w ith słowa „Get”, tak GetTotal() mapuje do Total

Każdy istniejący typ map już skonfigurowany

Zasadniczo, jeśli usunięto wszystkie „kropki” i „dostaje”, AutoMapper będzie mecz nazwy właściwości. Obecnie AutoMapper nie kończy się niepowodzeniem na niezgodnych typach , ale z innych powodów.

0

nie jestem całkowicie pewien, co chcesz, jak wynikach końcowych, jednak ....

przypadku porównywania właściwości na dwóch różnych typów można projekcie nazwy właściwości i odpowiadające im wartości do dwóch słownikach . I za pomocą tej informacji można dokonać sortowania/różnicy wartości właściwości.

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

Używanie tylko .NET 2.0 metody:

class Foo 
{ 
    public Guid Guid { get; } 
} 

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

Jeśli zajęcia nie realizują wspólny interfejs, trzeba będzie wdrożyć GetFooSubset indywidualnie dla każdego typu.

+0

.NET 2.0 nie obsługuje wyrażeń lambda ani nie ma metod rozszerzenia dodanych przez System.Linq. Twój przykład wymaga co najmniej .NET 3.0. –

+1

@Karim: FindAll nie jest metodą rozszerzenia dodawaną przez LINQ, ale zwykłą metodą dostarczaną przez klasę "List ", która jest częścią platformy .NET od wersji 2.0. Wyrażenia lambdy wymagają kompilatora C# 3.0, ale niekoniecznie .NET 3.0. – dtb

0

Thist powinien zasadniczo uzyskać to, co chcesz - ale może być lepiej z użyciem 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)); 
    } 
} 
+0

Miałem trochę przesady - edytowanie go. – dice

2

Ok, jeśli masz dostęp do modyfikacji tylko oryginalnych klas, aby dodać tam interfejs, Matthew miał go rozpoznać na. Trochę zwariowałem i zdefiniowałem pełne rozwiązanie, używając 2,0 anonimowych delegatów. (Myślę, że jestem uzależniony od 3.0 Lambda, w przeciwnym razie prawdopodobnie napisałbym to w pętlach foreach, jeśli wciąż korzystam z 2005 roku).

Zasadniczo utwórz interfejs o wspólnych właściwościach. Uczyń yoru dwie klasy implementujące interfejs. Utwórz wspólną listę odlaną jako interfejs, odrzuć i zgraj wartości na nową listę; usuń wszelkie niedopasowane elementy.

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

    } 
} 
+0

FYI ... OP powiedział 2.0; Zakładam 2005 VS, więc nie ma skrótów 3.0 VS 2008. – Nathan

+0

Działa to całkiem nieźle. Szkoda, że ​​nie mogę podać dwóch zaakceptowanych odpowiedzi. – Chris