У меня вопрос по 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"
Вышеуказанный результат ожидаем,
Но в случае если 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>
.
Есть предложения, пожалуйста?
Ответы:
Union возвращает
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
Ваш первоначальный образец работает, потому что целые числа являются типами значений и сравниваются по значению.
источник
x.Union(y)
такое же, какx.Concat(y).Distinct()
. Так что разница только в примененииDistinct
. Как Linq выбирает отдельные (т.е. разные) объекты в сцепленных последовательностях? В вашем примере кода (из вопроса) Linq сравнивает объекты по ссылке (то есть по адресу в памяти). Когда вы создаете новый объект с помощьюnew
оператора, он выделяет память по новому адресу. Итак, когда у вас будет четыре новых созданных объекта, адреса будут другими. И все объекты будут отличными. Таким образомDistinct
вернет все объекты из последовательности.Concat
буквально возвращает элементы из первой последовательности, за которыми следуют элементы из второй последовательности. Если вы используетеConcat
две последовательности из 2 элементов, вы всегда получите последовательность из 4 элементов.Union
по существуConcat
следуетDistinct
.В ваших первых двух случаях вы получите последовательности из двух элементов, потому что между ними каждая пара входных последовательностей имеет ровно два различных элемента.
В третьем случае вы получите последовательность из 4 элементов, потому что все четыре элемента в ваших двух входных последовательностях различны .
источник
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());
источник