2012-11-16 17 views
55

Mam pytanie na Union i Concat. Chyba oba zachowują się tak samo w przypadku List<T>.Union Vs Concat in Linq

var a1 = (new[] { 1, 2 }).Union(new[] { 1, 2 });    // O/P : 1 2 
var a2 = (new[] { 1, 2 }).Concat(new[] { 1, 2 });   // O/P : 1 2 1 2 

var a3 = (new[] { "1", "2" }).Union(new[] { "1", "2" });  // O/P : "1" "2" 
var a4 = (new[] { "1", "2" }).Concat(new[] { "1", "2" }); // O/P : "1" "2" "1" "2" 

Powyższy wynik oczekuje,

Ale Incase List<T> otrzymuję ten sam rezultat.

class X 
{ 
    public int ID { get; set; } 
} 

class X1 : X 
{ 
    public int ID1 { get; set; } 
} 

class X2 : X 
{ 
    public int ID2 { get; set; } 
} 

var lstX1 = new List<X1> { new X1 { ID = 10, ID1 = 10 }, new X1 { ID = 10, ID1 = 10 } }; 
var lstX2 = new List<X2> { new X2 { ID = 10, ID2 = 10 }, new X2 { ID = 10, ID2 = 10 } }; 

var a5 = lstX1.Cast<X>().Union(lstX2.Cast<X>());  // O/P : a5.Count() = 4 
var a6 = lstX1.Cast<X>().Concat(lstX2.Cast<X>()); // O/P : a6.Count() = 4 

Ale oba zachowują się tak samo jak List<T>.

Wszelkie sugestie proszę?

+1

Jeśli znasz różnicę między tymi dwoma sposobami, dlaczego niespodzianka wynik ty? Jest to bezpośrednia konsekwencja funkcjonalności metod. –

+0

@KonradRudolph, Co mam na myśli to podstęp z listy mogę użyć dowolnego "Union"/"Concat". Ponieważ oba zachowują się tak samo. –

+0

Nie, oczywiście, że nie.Nie zachowują się tak samo, jak pokazuje twój pierwszy przykład. –

Odpowiedz

68

Zwroty z Unii Distinct wartości. Domyślnie porównuje odniesienia przedmiotów. Twoje przedmioty mają różne referencje, więc wszystkie są uważane za różne. Podczas rzutowania na typ bazowy X odwołanie nie jest zmieniane.

Jeśli będzie zastąpić Equals i GetHashCode (używany do wyboru różne przedmioty), a następnie elementy nie będą porównywane przez odniesienie:

class X 
{ 
    public int ID { get; set; } 

    public override bool Equals(object obj) 
    { 
     X x = obj as X; 
     if (x == null) 
      return false; 
     return x.ID == ID; 
    } 

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

ale wszystkie swoje elementy mają różną wartość ID. Tak więc wszystkie pozycje są nadal uważane za różne. Jeśli będzie podać kilka pozycji z tego samego ID wtedy widać różnicę między Union i Concat:

var lstX1 = new List<X1> { new X1 { ID = 1, ID1 = 10 }, 
          new X1 { ID = 10, ID1 = 100 } }; 
var lstX2 = new List<X2> { new X2 { ID = 1, ID2 = 20 }, // ID changed here 
          new X2 { ID = 20, ID2 = 200 } }; 

var a5 = lstX1.Cast<X>().Union(lstX2.Cast<X>()); // 3 distinct items 
var a6 = lstX1.Cast<X>().Concat(lstX2.Cast<X>()); // 4 

początkowej przykładowe prace, ponieważ całkowite są typy wartości i są one porównywane przez wartość.

+2

Nawet jeśli nie było porównywania odniesień, ale np. identyfikatory w obrębie, nadal będą cztery elementy, ponieważ identyfikatory są różne. – Rawling

+0

@Rawling zgodzić się, również prawdziwe –

+0

@Swani nie, nie są. Myślę, że nie zmieniłeś identyfikatora pierwszego elementu w drugim zbiorze, jak stwierdziłem powyżej –

29

Concat dosłownie zwraca pozycje z pierwszej sekwencji, po której następują elementy z drugiej sekwencji. Jeśli użyjesz Concat w dwóch 2-elementowych sekwencjach, zawsze otrzymasz sekwencję 4-itemową.

Union to zasadniczo Concat, po którym następuje Distinct.

W twoich pierwszych dwóch przypadkach kończysz z sekwencjami o 2 pozycjach, ponieważ między nimi każda para wprowadzonych squencji ma dokładnie dwa różne elementy.

W trzecim przypadku kończy się sekwencja z 4 pozycjami, ponieważ wszystkie cztery elementy w dwóch wejściowych sekwencjach są różne.

12

Union i Concat zachowują się tak samo, ponieważ Union nie może wykryć duplikatów bez niestandardowego IEqualityComparer<X>. Po prostu szukam, czy oba są tym samym odniesieniem.

public class XComparer: IEqualityComparer<X> 
{ 
    public bool Equals(X x1, X x2) 
    { 
     if (object.ReferenceEquals(x1, x2)) 
      return true; 
     if (x1 == null || x2 == null) 
      return false; 
     return x1.ID.Equals(x2.ID); 
    } 

    public int GetHashCode(X x) 
    { 
     return x.ID.GetHashCode(); 
    } 
} 

Teraz można go używać na przeciążenie Union:

var comparer = new XComparer(); 
a5 = lstX1.Cast<X>().Union(lstX2.Cast<X>(), new XComparer());