J'ai une question sur Union
et Concat
. Je suppose que les deux se comportent de la même manière en cas de List<T>
.
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"
Les résultats ci-dessus sont attendus,
Mais au cas où j'obtiens le List<T>
même résultat.
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
Mais les deux se comportent de la même manière List<T>
.
Des suggestions s'il vous plaît?
Réponses:
Union renvoie des
Distinct
valeurs. Par défaut, il comparera les références des articles. Vos articles ont des références différentes, ils sont donc tous considérés comme différents. Lorsque vous transtypez en type de baseX
, la référence n'est pas modifiée.Si vous remplacez
Equals
etGetHashCode
(utilisé pour sélectionner des éléments distincts), les éléments ne seront pas comparés par référence: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(); } }
Mais tous vos articles ont une valeur différente de
ID
. Donc, tous les éléments sont toujours considérés comme différents. Si vous fournissez plusieurs éléments avec le même,ID
vous verrez la différence entreUnion
etConcat
: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
Votre exemple initial fonctionne, car les entiers sont des types valeur et ils sont comparés par valeur.
la source
x.Union(y)
est le même quex.Concat(y).Distinct()
. Donc, la différence ne concerne que l'applicationDistinct
. Comment Linq sélectionne des objets distincts (c'est-à-dire différents) dans des séquences concaténées? Dans votre exemple de code (à partir de la question), Linq compare les objets par référence (c'est-à-dire adresse en mémoire). Lorsque vous créez un nouvel objet via l'new
opérateur, il alloue de la mémoire à une nouvelle adresse. Ainsi, lorsque vous avez quatre nouveaux objets créés, les adresses seront différentes. Et tous les objets seront distincts. AinsiDistinct
retournera tous les objets de la séquence.Concat
renvoie littéralement les éléments de la première séquence suivis des éléments de la deuxième séquence. Si vous utilisezConcat
deux séquences de 2 éléments, vous obtiendrez toujours une séquence de 4 éléments.Union
est essentiellementConcat
suivi deDistinct
.Dans vos deux premiers cas, vous vous retrouvez avec des séquences de 2 éléments car, entre eux, chaque paire de séquences d'entrée a exactement deux éléments distincts.
Dans votre troisième cas, vous vous retrouvez avec une séquence de 4 éléments car les quatre éléments de vos deux séquences d'entrée sont distincts .
la source
Union
etConcat
se comportent de la même manière carUnion
ne peuvent pas détecter les doublons sans une coutumeIEqualityComparer<X>
. Il s'agit simplement de voir si les deux sont la même référence.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(); } }
Vous pouvez maintenant l'utiliser dans la surcharge de
Union
:var comparer = new XComparer(); a5 = lstX1.Cast<X>().Union(lstX2.Cast<X>(), new XComparer());
la source