2013-06-07 23 views
10

Próbuję scalić dwie listy przy użyciu list.Union w LinqPad, ale nie mogę uruchomić go i chciałem sprawdzić, czy moje zrozumienie jest poprawne.Usuwanie duplikatów podczas scalania list za pomocą Unii w LINQ

Biorąc pod uwagę to prosta klasa:

public class Test 
{ 
    public int Id { get; set;} 
    public int field1 { get; set; } 

    public bool Equals(Test other) 
    {   
     return this.Id.Equals(other.Id); 
    } 
} 

I dwie listy wypełniana tak:

List<Test> list = new List<Test>(); 
list.Add(new Test { Id = 1, field1 = 1}); 
list.Add(new Test { Id = 1, field1 = 2}); 
list.Add(new Test { Id = 2, field1 = 3}); 
list.Add(new Test { Id = 2, field1 = 4}); 

List<Test> list2 = new List<Test>(); 
list2.Add(new Test { Id = 1, field1 = 1}); 
list2.Add(new Test { Id = 1, field1 = 2}); 
list2.Add(new Test { Id = 2, field1 = 3}); 
list2.Add(new Test { Id = 2, field1 = 4}); 

I spróbuj: var mergedList = list.Union(list2).ToList(); i wyjściowe dane za pomocą prostego foreach pętlę i uzyskać ten wynik:

ID: 1 -------- 1 
ID: 1 -------- 2 
ID: 2 -------- 3 
ID: 2 -------- 4 
ID: 1 -------- 1 
ID: 1 -------- 2 
ID: 2 -------- 3 
ID: 2 -------- 4 

Byłem pod wrażeniem, że Union należy usunąć duplikaty powrotu:

ID: 1 -------- 1 
ID: 1 -------- 2 
ID: 2 -------- 3 
ID: 2 -------- 4 

robię coś źle albo ja źle?

Czy powinien on działać bez jawnego przesłonięcia metody Equals w klasie Test?

Dzięki

+1

powinieneś przeczytać tę stronę [dokumentacja] (http://msdn.microsoft.com/en-us/library/system.object.gethashcode. aspx). – Jon

+0

@ Jon. Będę. Dziękuję Ci. – davy

Odpowiedz

12

W twoim przypadku wystarczy zdefiniować niektóre metody, że nic nie wie o LINQ. To tak, jakby stworzyć metodę bool HeyEquateMeWith(Test other) i oczekiwać, że LINQ wywoła ją podczas wykonywania operacji ustawiania.

Trzeba zdefiniować klasę w następujący sposób (ręcznymObject „s Equals i GetHashCode metody):

public class Test 
{ 
    public int Id { get; set;} 
    public int field1 { get; set; } 

    public override bool Equals(object other) //note parameter is of type object 
    {   
     Test t = other as Test; 
     return (t != null) ? Id.Equals(t.Id) : false; 
    } 

    public override int GetHashCode() 
    { 
     return Id.GetHashCode(); 
    } 
} 

Teraz Union wezwie swoich nadpisane Equals i GetHashCode metod. Powinieneś również ALWAYS zastąpić GetHashCode, gdy zastąpisz metodę Equals.

+0

Dzięki. Tak, myślałem, że mogę zostawić GetHashCOde na przykład, ale jest to wymagane. Jak działa domyślny porównywal dla Unii lub czy zawsze muszę zastąpić Equals i GethashCode? – davy

+1

@davy domyślny comparer będzie sprawdzał tożsamość obiektu (ta sama funkcjonalność co operator '=='). Jeśli zmienisz klasę 'Test' na' struct', domyślna równość będzie miała wartość semantyki (porównaj zawartość dwóch struktur, w tym 'Id' i' field1'). –

+0

Rozumiem. Dziękuję bardzo za Twoją pomoc. – davy

1

Można utworzyć klasę wykonawczą

IEqualityComparer<Test> 

Czy ta klasa określić Równa i GetHashCode po niej można zdać ten porównywarka wam metody unijnej Właśnie tak:

public class MyComparer:IEqualityComparer<Test>{ 
//Equals and GetHashCode 
} 

var mergedList = list.Union(list2, new MyComparer()).ToList(); 
0

Ty możliwe, spróbuj czegoś takiego, jeśli nie jesteś zadowolony z domyślnego porównywalnika (który z kolei wykorzystuje metodę GetHashCode, o której wspomniał @IlyaIvanov):

// get all items that "other than in first list", so Where() and Any() are our filtering expressions 
var delta = list2.Where(x2 => !list.Any(x1 => (x1.Id == x2.Id) && (x1.field1 == x2.field1))); 

// now let merge two enumerables that have nothing "equal" between them 
var merged = list.Union(delta).ToList();