Союз против конкатенацию в LINQ


у меня вопрос 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>.

любые предложения, пожалуйста?

3 62

3 ответа:

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

ваш первоначальный пример работает, потому что целые числа являются типами значений и они сравниваются по значению.

Concat буквально возвращает элементы из первой последовательности, затем элементы второй последовательности. Если вы используете Concat на двух последовательностях из 2 элементов вы всегда получите последовательность из 4 элементов.

Union по сути Concat следовал по Distinct.

в первых двух случаях вы получаете последовательности из 2 элементов, потому что между ними каждая пара входных квадратов имеет ровно два разных элемента.

в третьем случае, вы в конечном итоге с 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());