2012-11-16 18 views
55

Ho una domanda su Union e Concat. Suppongo che entrambi si comportino allo stesso modo nel caso di 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" 

Il risultato di cui sopra sono attesi,

Ma Incase di List<T> sto ottenendo lo stesso risultato.

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 

Ma entrambi si comportano lo stesso in caso di List<T>.

Qualche suggerimento, per favore?

+1

Se si conosce la differenza tra questi due metodi, perché il risultato vi sorprenderà? È una conseguenza diretta della funzionalità dei metodi. –

+0

@KonradRudolph, Quello che intendo è nell'Elenco posso usare qualsiasi 'Unione'/'Concat'. Perché entrambi si comportano allo stesso modo. –

+0

No, ovviamente no.Non si comportano allo stesso modo, come mostra il tuo primo esempio. –

risposta

68

valori restituiti dall'Unione Distinct. Per impostazione predefinita confronterà i riferimenti di elementi. I tuoi articoli hanno riferimenti diversi, quindi sono tutti considerati diversi. Quando si esegue il cast sul tipo di base X, il riferimento non viene modificato.

Se si ignorare Equals e GetHashCode (utilizzato per selezionare le voci distinte), quindi gli elementi non saranno confrontati con riferimento:

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

Ma tutti gli oggetti hanno un valore diverso di ID. Quindi tutti gli articoli sono ancora considerati diversi. Se vi fornirà diversi elementi con lo stesso ID allora vedrete differenza tra Union e 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 

tue opere iniziali del campione, perché interi sono tipi di valore e si sono confrontati per valore.

+2

Anche se non stava confrontando i riferimenti ma ad es. gli ID all'interno, ci sarebbero ancora quattro elementi come gli ID sono diversi. – Rawling

+0

@Rawling d'accordo, anche vero –

+0

@Swani no, non lo sono. Penso che non hai cambiato l'ID del primo oggetto nella seconda raccolta, come ho affermato sopra –

29

Concat restituisce letteralmente gli elementi dalla prima sequenza seguita dagli elementi dalla seconda sequenza. Se si utilizza Concat su due sequenze di 2 elementi, si otterrà sempre una sequenza di 4 elementi.

Union è essenzialmente Concat seguito da Distinct.

Nei primi due casi, si finiscono con sequenze di 2 elementi perché, tra loro, ogni coppia di input ha esattamente due distinti elementi.

Nel terzo caso, si finisce con una sequenza di 4 elementi perché tutti e quattro gli elementi nelle due sequenze di input sono distinti.

12

Union e Concat si comportano allo stesso dato Union non in grado di rilevare i duplicati senza un personalizzato IEqualityComparer<X>. Sta solo guardando se entrambi sono lo stesso riferimento.

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

Ora è possibile utilizzarlo in un sovraccarico di Union:

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