比较两个序列是否相等

本文关键字:是否 两个 比较 | 更新日期: 2023-09-27 18:25:19

在因其标题将其标记为重复之前,请考虑以下简短程序:

static void Main()
{
    var expected = new List<long[]> { new[] { Convert.ToInt64(1), Convert.ToInt64(999999) } };
    var actual = DoSomething();
    if (!actual.SequenceEqual(expected)) throw new Exception();
}
static IEnumerable<long[]> DoSomething()
{
    yield return new[] { Convert.ToInt64(1), Convert.ToInt64(999999) };
}

我有一个返回 long 类型的数组序列的方法。为了测试它,我编写了一些类似于 Main 中的测试代码。

但是我得到了例外,但我不知道为什么。预期的序列不应该与实际返回的序列相当,还是我错过了什么?

对我来说,方法和epxected似乎都只包含一个包含 long 类型数组的单个元素,不是吗?

编辑:那么我如何实现不获取异常含义来比较枚举中的元素以返回相等性?

比较两个序列是否相等

实际问题是您正在比较两个long[],并且Enumerable.SequenceEquals将使用一个ObjectEqualityComparer<Int64[]>(您可以通过检查EqualityComparer<long[]>.Default看到这是Enumerable.SequenceEquals内部使用的内容(,它将比较这两个数组的引用,而不是存储在数组中的实际,这显然是不一样的。

要解决此问题,您可以编写一个自定义EqualityComparer<long[]>

static void Main()
{
    var expected = new List<long[]> 
                       { new[] { Convert.ToInt64(1), Convert.ToInt64(999999) } };
    var actual = DoSomething();
    if (!actual.SequenceEqual(expected, new LongArrayComparer()))
        throw new Exception();
}
public class LongArrayComparer : EqualityComparer<long[]>
{
    public override bool Equals(long[] first, long[] second)
    {
        return first.SequenceEqual(second);
    }
    // GetHashCode implementation in the courtesy of @JonSkeet
    // from http://stackoverflow.com/questions/7244699/gethashcode-on-byte-array
    public override int GetHashCode(long[] arr)
    {
        unchecked
        {
            if (array == null)
            {
                return 0;
            }
            int hash = 17;
            foreach (long element in arr)
            {
                hash = hash * 31 + element.GetHashCode();
            }
            return hash;
        }
    }
}

不,你的序列相等!

让我们删除序列位,只取每个项目的第一个元素中的内容

var firstExpected = new[] { Convert.ToInt64(1), Convert.ToInt64(999999) };
var firstActual = new[] { Convert.ToInt64(1), Convert.ToInt64(999999) };
Console.WriteLine(firstExpected == firstActual); // writes "false"

上面的代码比较两个单独的数组是否相等。相等不检查数组的内容,它检查引用的相等性。

使用SequenceEquals的代码本质上是在做同样的事情。它检查可枚举项中每个元素的每个情况下的引用。

SequenceEquals测试序列中的元素是否相同。枚举中的元素是 long[] 类型,所以我们实际上将两个不同的数组(但包含相同的元素(相互比较,这是通过比较它们的引用而不是它们的实际值来痴迷地完成的。

所以我们在这里实际检查的是这个expected[0] == actual[0]而不是expected[0].SequqnceEquals(actual[0])

这是 obbiosuly 返回false因为两个数组共享不同的引用。

如果我们使用 SelectMany 扁平层次结构,我们会得到我们想要的:

if (!actual.SelectMany(x => x).SequenceEqual(expected.SelectMany(x => x))) throw new Exception();

编辑:

基于这种方法,我找到了另一种优雅的方法来检查expected中的所有元素是否也包含在actual中:

if (!expected.All(x => actual.Any(y => y.SequenceEqual(x)))) throw new Exception();

这将搜索expected内是否有与当前列表顺序相同的列表actual子列表。这似乎更聪明,因为我们不需要任何自定义EqualityComparer,也没有奇怪的哈希码实现。