工会对林卡的联合会


86

我对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>

有什么建议吗?


1
如果您知道这两种方法之间的区别,为什么结果会让您感到惊讶?这是方法功能的直接结果。
康拉德·鲁道夫

@KonradRudolph,我的意思是在List <T>的情况下,我可以使用任何一个'Union'/'Concat'。因为两者的行为相同。
Prasad Kanaparthi 2012年

不,显然不是。正如您的第一个示例所示,它们的行为不同。
康拉德·鲁道夫

在您的示例中,所有ID都不相同。
Jim Mischel

@JimMischel,编辑了我的帖子。即使具有相同的值,它的行为也相同。
Prasad Kanaparthi 2012年

Answers:


110

联合返回Distinct值。默认情况下,它将比较项目的引用。您的商品有不同的参考,因此它们都被认为是不同的。当您转换为基本类型时X,引用不会更改。

如果您将覆盖EqualsGetHashCode(用于选择不同的项目),则不会通过引用比较项目:

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

您的初始样本有效,因为整数是值类型,并且它们按值进行比较。


3
即使不是比较参考,而是例如其中的ID,由于ID不同,仍然会有四项。
罗林

@Swani不,不是。我认为您没有更改第二个收藏集中的第一个商品的ID,就像我上面所说的那样
Sergey Berezovskiy 2012年

@Swani,那么您就没有像上面所说的那样覆盖Equals和GetHashCode
Sergey Berezovskiy 2012年

@lazyberezovsky,我同意你的回答。但是我对这些评论仍然不满意。如果执行我的示例代码,则可以看到“ a5”和“ a6”的相同结果。我不是在寻找解决方案。但是,为什么“ Concat”和“ Union”在这种情况下表现相同。请回复。
Prasad Kanaparthi 2012年

3
@Swani很抱歉,是afk。x.Union(y)与相同x.Concat(y).Distinct()。因此,区别仅在于申请Distinct。Linq如何选择串联序列中的不同(即不同)对象?在您的示例代码(来自问题)中,Linq通过引用(即内存中的地址)比较对象。通过new运算符创建新对象时,它会在新地址处分配内存。因此,当您有四个新创建的对象时,地址将不同。并且所有对象都是不同的。因此Distinct将返回序列中的所有对象。
谢尔盖·贝雷佐夫斯基

48

Concat从字面上返回第一个序列中的项目,然后返回第二个序列中的项目。如果Concat在两个2项序列上使用,则始终会得到4项序列。

Union本质上ConcatDistinct

在前两种情况下,您最终会得到2个项目的序列,因为在它们之间,每对输入序列都恰好有两个不同的项目。

在第三种情况下,由于两个输入序列中的所有四个项目都是不同的,因此最终得到了4个项目的序列。


14

UnionConcat具有相同的行为,因为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()); 
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.