Tengo una pregunta sobre Union
y Concat
. Supongo que ambos se comportan igual en el caso 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"
Se espera el resultado anterior,
Pero en caso de que obtengo el List<T>
mismo resultado.
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
Pero ambos se comportan igual en el caso de List<T>
.
¿Alguna sugerencia por favor?
Respuestas:
Union devuelve
Distinct
valores. De forma predeterminada, comparará referencias de artículos. Sus artículos tienen diferentes referencias, por lo que todos se consideran diferentes. Cuando lanza a tipo baseX
, la referencia no cambia.Si anula
Equals
yGetHashCode
(utilizado para seleccionar elementos distintos), los elementos no se compararán por referencia: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(); } }
Pero todos sus artículos tienen un valor diferente de
ID
. Así que todos los elementos se siguen considerando diferentes. Si proporciona varios artículos con el mismoID
, verá la diferencia entreUnion
yConcat
: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
Su muestra inicial funciona, porque los números enteros son tipos de valor y se comparan por valor.
fuente
x.Union(y)
es lo mismo quex.Concat(y).Distinct()
. Entonces la diferencia es solo con la aplicaciónDistinct
. ¿Cómo selecciona Linq objetos distintos (es decir, diferentes) en secuencias concatenadas? En su código de muestra (de la pregunta) Linq compara objetos por referencia (es decir, dirección en la memoria). Cuando crea un nuevo objeto a través delnew
operador, asigna memoria en la nueva dirección. Entonces, cuando tenga cuatro nuevos objetos creados, las direcciones serán diferentes. Y todos los objetos serán distintos. PorDistinct
lo tanto , devolverá todos los objetos de la secuencia.Concat
literalmente devuelve los elementos de la primera secuencia seguidos de los elementos de la segunda secuencia. Si utilizaConcat
en dos secuencias de 2 elementos, siempre obtendrá una secuencia de 4 elementos.Union
esConcat
seguido esencialmente porDistinct
.En sus dos primeros casos, termina con secuencias de 2 elementos porque, entre ellos, cada par de secuencias de entrada tiene exactamente dos elementos distintos.
En su tercer caso, termina con una secuencia de 4 elementos porque los cuatro elementos en sus dos secuencias de entrada son distintos .
fuente
Union
y seConcat
comportan igual yaUnion
que no se pueden detectar duplicados sin una costumbreIEqualityComparer<X>
. Solo busca si ambos son la misma referencia.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(); } }
Ahora puedes usarlo en la sobrecarga de
Union
:var comparer = new XComparer(); a5 = lstX1.Cast<X>().Union(lstX2.Cast<X>(), new XComparer());
fuente