C# LINQ Intersect、Union中的集合优先级,使用IEqualityComparer

C# LINQ Intersect、Union中的集合优先级,使用IEqualityComparer,c#,linq,union,intersect,iequalitycomparer,C#,Linq,Union,Intersect,Iequalitycomparer,如果我有两个类型为T的集合,一个IEqualityComparer比较它们的属性子集,那么产生的相交或并集元素来自哪个集合 到目前为止,我运行的测试表明: col1 win中的项目 如果col1或col2本身包含重复项(由比较器定义),则第一个条目(在col1中,然后是col2)获胜 我知道这不应该是一个问题,因为(根据定义)我应该将结果对象视为相等的。我突然想到,使用带有自定义比较器的Union可能比使用等效的Join要简洁一些——尽管只有在保证上述假设的情况下,这才是正确的 cl

如果我有两个类型为T的集合,一个IEqualityComparer比较它们的属性子集,那么产生的相交或并集元素来自哪个集合

到目前为止,我运行的测试表明:

  • col1 win中的项目
  • 如果col1或col2本身包含重复项(由比较器定义),则第一个条目(在col1中,然后是col2)获胜
我知道这不应该是一个问题,因为(根据定义)我应该将结果对象视为相等的。我突然想到,使用带有自定义比较器的Union可能比使用等效的Join要简洁一些——尽管只有在保证上述假设的情况下,这才是正确的

    class DummyComparer : IEqualityComparer<Dummy>
    {
        public bool Equals(Dummy x, Dummy y)
        {
            return x.ID == y.ID;
        }

        public int GetHashCode(Dummy obj)
        {
            return obj.ID.GetHashCode();
        }
    }

    class Dummy
    {
        public int ID { get; set; }
        public string Name { get; set; }
    }

    [Test]
    public void UnionTest()
    {
        var comparer = new DummyComparer();

        var d1 = new Dummy { ID = 0, Name = "test0" };
        var d2 = new Dummy { ID = 0, Name = "test1" };
        var d3 = new Dummy { ID = 1, Name = "test2" };
        var d4 = new Dummy { ID = 1, Name = "test3" };

        var col1 = new Dummy[] { d1, d3 };
        var col2 = new Dummy[] { d2, d4 };

        var x1 = col1.Union(col2, comparer).ToList();
        var x2 = col2.Union(col1, comparer).ToList();

        var y1 = col1.Except(col2, comparer).ToList();
        var y2 = col2.Except(col1, comparer).ToList();

        var z1 = col1.Intersect(col2, comparer).ToList();
        var z2 = col2.Intersect(col1, comparer).ToList();

        Assert.AreEqual(2, x1.Count);
        Assert.Contains(d1, x1);
        Assert.Contains(d3, x1);

        Assert.AreEqual(2, x2.Count);
        Assert.Contains(d2, x2);
        Assert.Contains(d4, x2);

        Assert.AreEqual(0, y1.Count);
        Assert.AreEqual(0, y2.Count);

        Assert.AreEqual(2, z1.Count);
        Assert.Contains(d1, z1);
        Assert.Contains(d3, z1);

        Assert.AreEqual(2, z2.Count);
        Assert.Contains(d2, z2);
        Assert.Contains(d4, z2);
    }
class DummyComparer:IEqualityComparer
{
公共布尔等于(虚拟x,虚拟y)
{
返回x.ID==y.ID;
}
public int GetHashCode(虚拟对象)
{
返回obj.ID.GetHashCode();
}
}
类虚拟
{
公共int ID{get;set;}
公共字符串名称{get;set;}
}
[测试]
公共测试()
{
var comparer=new DummyComparer();
var d1=newdummy{ID=0,Name=“test0”};
var d2=newdummy{ID=0,Name=“test1”};
var d3=newdummy{ID=1,Name=“test2”};
var d4=newdummy{ID=1,Name=“test3”};
var col1=新的虚拟[]{d1,d3};
var col2=新的虚拟[]{d2,d4};
var x1=col1.Union(col2,comparer.ToList();
var x2=col2.Union(col1,comparer.ToList();
var y1=col1.Except(col2,comparer.ToList();
var y2=col2.Except(col1,comparer.ToList();
var z1=col1.Intersect(col2,comparer.ToList();
var z2=col2.Intersect(col1,comparer.ToList();
断言.AreEqual(2,x1.Count);
包含(d1,x1);
包含(d3,x1);
断言.AreEqual(2,x2.Count);
包含(d2,x2);
包含(d4,x2);
Assert.AreEqual(0,y1.Count);
Assert.AreEqual(0,y2.Count);
Assert.AreEqual(2,z1.Count);
断言包含(d1,z1);
Assert.Contains(d3,z1);
arest.AreEqual(2,z2.Count);
包含(d2,z2);
Assert.Contains(d4,z2);
}

第一个系列应始终获胜

:

枚举此方法返回的对象时,Union 按顺序枚举第一个和第二个并生成 还没有屈服

下面是
Union
(ILSPY.NET 4)的实现,首先枚举第一个集合:

// System.Linq.Enumerable
private static IEnumerable<TSource> UnionIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
{
    Set<TSource> set = new Set<TSource>(comparer);
    foreach (TSource current in first)
    {
        if (set.Add(current))
        {
            yield return current;
        }
    }
    foreach (TSource current2 in second)
    {
        if (set.Add(current2))
        {
            yield return current2;
        }
    }
    yield break;
}

我想说看看MSDN文档,但它实际上是关于
Intersect
的。也许你可以看看,它详细介绍了最初的实现。@Rawling:有意思,你说得对。我已经用ILSpy查看了
Intersect
,它首先列举了第二个集合,然后列举了第一个集合,即使是以相反的方式记录的。原因可能是什么?编辑实际上Jon Skeet也提到了这个“谎言”:(用他的话来说:“这显然是不正确的”)@Tim关于哪些元素<代码>相交返回也不明确-我把它理解为“在第二个序列中标记元素,并返回它们”,但你显然是从另一个角度看的。(是的,这就是我链接Edulink时想到的页面。)@Rawling:是的,实现不会改变行为。它仍然会从第一个序列返回对象,即使它首先枚举了第二个序列。我已经更新了我的答案。令人困惑。@trilson86:更新了我的答案,但即使
Intersect
的行为与文档中的不同,它仍然会返回第一个集合中的项目。这是一个奇怪的实现-感谢您指出。
// System.Linq.Enumerable
private static IEnumerable<TSource> IntersectIterator<TSource>(IEnumerable<TSource> first, IEnumerable<TSource> second, IEqualityComparer<TSource> comparer)
{
    Set<TSource> set = new Set<TSource>(comparer);
    foreach (TSource current in second)
    {
        set.Add(current);
    }
    foreach (TSource current2 in first)
    {
        if (set.Remove(current2))
        {
            yield return current2;
        }
    }
    yield break;
}