Union Vs Concat в Линк
У меня вопрос о Union
и Concat
. Я думаю, что оба ведут себя одинаково в случае 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"
Ожидаемый результат,
Но incase List<T>
Я получаю тот же результат.
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
Но оба ведут себя одинаково с тем, что List<T>
.
Любые предложения, пожалуйста?
Ответы
Ответ 1
Союз возвращает значения Distinct
. По умолчанию он будет сравнивать ссылки на элементы. У ваших предметов разные ссылки, поэтому все они считаются разными. При нажатии на базовый тип X
ссылка не изменяется.
Если вы переопределите Equals
и GetHashCode
(используется для выбора отдельных элементов), то элементы не будут сравниваться по ссылке:
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();
}
}
Но все ваши предметы имеют разное значение ID
. Таким образом, все предметы по-прежнему считаются разными. Если вы предоставите несколько элементов с тем же ID
, тогда вы увидите разницу между Union
и 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
Ваш исходный образец работает, поскольку целые числа являются типами значений, и они сравниваются по значению.
Ответ 2
Concat
буквально возвращает элементы из первой последовательности, за которыми следуют элементы из второй последовательности. Если вы используете Concat
для двух последовательностей из 2 элементов, вы всегда будете получать последовательность из 4 элементов.
Union
по существу Concat
, за которым следует Distinct
.
В первых двух случаях вы получаете 2-позиционные последовательности, потому что между ними каждая пара входных скруглений имеет ровно два разных элемента.
В вашем третьем случае вы получаете последовательность из 4 элементов, потому что все четыре элемента в ваших двух входных последовательностях различны.
Ответ 3
Union
и Concat
ведут себя одинаково, поскольку Union
не может обнаружить дубликаты без пользовательского IEqualityComparer<X>
. Это просто выглядит, если обе эти же ссылки.
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();
}
}
Теперь вы можете использовать его при перегрузке Union
:
var comparer = new XComparer();
a5 = lstX1.Cast<X>().Union(lstX2.Cast<X>(), new XComparer());