LINQ Except将第一个集合的项目发送到IEqualityComparer Equals方法

时间:2019-01-21 12:41:02

标签: c# linq

我正在尝试实现IEqualityComparer<T>以便将Except与复杂类型的集合一起使用。我在调试时遇到了一个奇怪的(或我不知道的平常事物)问题。我有两个馆藏的物品数量不同,如下所示。

{A, B, C, D, E}.Except({A, B}, new CustomComparer()).ToList()

通过添加ToList(),我可以调试覆盖的Equals(x, y)。直到项目C都像预期的那样工作,直到项目B之后,除外向C发送DEquals(x, y)public SubmoduleListComparer( Action<FormerGsdmlComparison.SubModuleListDifferenceContainer, string, string> callBack, string firstFileName, string secondFilename) { DifferenceHighlighter = callBack; m_FirstFileName = firstFileName; m_SecondFileName = secondFilename; } public bool Equals(Submodule x, Submodule y) { bool areEqual = true; if (x == null || y == null) return false; var submoduleDifferences = new FormerGsdmlComparison.SubModuleListDifferenceContainer { file1 = new FormerGsdmlComparison.Submodule { orderNumber = x.OrderNumber, submoduleId = x.Id, submoduleIdentNumber = x.SubmoduleIdentNumber } }; if (x.Id != y.Id) { submoduleDifferences.file2.submoduleId = y.Id; areEqual = false; } if (x.OrderNumber != y.OrderNumber) { submoduleDifferences.file2.orderNumber = y.OrderNumber; areEqual = false; } if (x.SubmoduleIdentNumber != y.SubmoduleIdentNumber) { submoduleDifferences.file2.submoduleIdentNumber = y.SubmoduleIdentNumber; areEqual = false; } if (!areEqual) { DifferenceHighlighter(submoduleDifferences, m_FirstFileName, m_SecondFileName); } return areEqual; } ,所以我无法区分这些元素属于第一个集合或其中一个属于第二个。

下面是我的IEqualityComparer实现。 DifferenceHighlighter是一种回调方法,为我提供了在调用者位置收集差异的方法。

Except()

如上所述,我期望null在对第二个集合的项目进行迭代时发送Equals(x, y)。相反,它将两个元素从第一个集合发送到first是LINQ Except的默认行为,我应该进行更多检查还是缺少什​​么?

编辑

second集合包含51个元素,而first集合仅包含7个元素。将两个集合中的7个项目发送到Equals(x,y)之后;除了开始发送second集合中的顺序项目。例如:

This is the debug view for first items on both collections

以上正是我所期望的。前两项属于Equals方法。但是在第7次迭代之后; items on Equals(x, y) like that

first集合没有这些项目。以上各项是 $('.portfolio-slider').owlCarousel({ loop:true, autoplay:true, autoplayTimeout:4000, navText:['<i class="ion-chevron-left"></i>','<i class="ion-chevron-right"></i>'], nav:true, responsive:{ 0:{ items:1 }, } }) $('.clients-active').owlCarousel({ loop:true, navText:['<i class="ion-chevron-left"></i>','<i class="ion-chevron-right"></i>'], nav:true, autoplay:true, autoplaySpeed:800, autoplayTimeout:2000, animateOut: 'fadeOut', responsive:{ 0:{ items:2 }, 450:{ items:3 }, 768:{ items:4 }, 1000:{ items:6 } } }) 集合的第8和第9个元素。因此,我的DifferenceHighlighter假定这些是两个集合之间的差异。

2 个答案:

答案 0 :(得分:3)

这是预期行为; Except使用 set (而不是 bags )(仅包含uinque物品)进行操作;因此Except仅返回不同的项目:

var demo = new int[] {1, 1} 
  .Except(new int[0])
  .ToList();

Console.Write(string.Join(" ", demo));

结果:

1

在您的情况下,Except测试项CD(来自第一个集合的都是)的目的是:确保返回仅与不同的项目:

https://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,e289e6c98881b2b8

static IEnumerable<TSource> ExceptIterator<TSource>(
  IEnumerable<TSource> first, 
  IEnumerable<TSource> second, 
  IEqualityComparer<TSource> comparer) {
    Set<TSource> set = new Set<TSource>(comparer);

    foreach (TSource element in second) 
        set.Add(element);

    foreach (TSource element in first)
        // Here Except tries adding element from first 
        // and have to compare if the element has been in set already.
        // in your case 'D' will be tested on A, B (which are in second)
        // and 'C' which has been added earlier
        if (set.Add(element)) 
            yield return element;
}

如果您想要first中的“ 所有个项目(包含重复的 ),但{{1}中出现 的项目除外”,您可以手动创建second并放置一个简单的HashSet<T>

Where

答案 1 :(得分:1)

事实证明,我完全误解了Except()的目的和用法。正如克里斯(Chris)的评论和德米特里(Dmitry)对Except()的解释的回答一样,最好使用Zip()迭代两个集合,检测差异并将结果合并到另一个集合(或其他无数选项)中。 Except确实可以代表其意思。经过对Zip()简单代码示例的快速调查,该示例也符合我的条件,如下所示:

foreach (var submoduleListPairs in firstFile.SubmoduleList.Zip(secondFile.SubmoduleList, (x, y) => new { x, y }))
{
    if (submoduleListPairs.x != null && submoduleListPairs.y != null)
    {
        if (submoduleListPairs.x.SubmoduleIdentNumber != submoduleListPairs.y.SubmoduleIdentNumber)
        {
            //Add differences to result collection
        }
        //Do other comparisons like below
    }
    else if (submoduleListPairs.x == null)
    {
        //Notate that second collection contains an item which first one not on result collection
    }
    else if (submoduleListPairs.y == null)
    {
        //Notate that first collection contains an item which second one not on result collection 
    }
}

这可能不是Zip()的最佳用法,但我想展示一下它如何解决我的问题,以及为什么我不应该使用Except()进行比较。我认为我一直坚持认为IEqualityComparer和Except是LINQ比较问题的方法。

最后编辑

Zip()的想法令人鼓舞,但是如果其中一个集合由于其目的而超出项目范围(合并集合),则内置Zip()会停止。因此,我进行了更深入的搜索,发现了一个很棒的SO问题和this answer。即使没有投票,也可以大大简化上述答案。