比较两个序列是否相等

时间:2015-12-02 15:31:00

标签: c# ienumerable yield

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

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类型数组的单个元素,不是吗?

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

3 个答案:

答案 0 :(得分:5)

实际问题是,您要比较两个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;
        }
    }
}

答案 1 :(得分:4)

不,你的序列相等!

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

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

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

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

答案 2 :(得分:0)

SequenceEquals测试序列中的元素是否相同。枚举中的元素是long[]类型,因此我们实际上比较了两个不同的数组(但是包含相同的元素),这是通过比较它们的引用而不是它们的实际值来进行的。

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

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

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

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

编辑:

基于this approach,我找到了另一种优雅的方式来检查expected中是否还包含actual中的所有元素:

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

这将搜索expected中的actual内是否存在与EqualityComparer内的列表顺序相同的列表。这似乎更聪明,因为我们不需要任何自定义// define the function, and its arguments: function uniquelyClassed (arr, cName) { // arr: Array of elements to filter, // cName: String, the class-name you wish // to filter by // checking that the passed Array is in fact // an Array: if (arr instanceof Array) { // filtering the passed Array, // 'n' is the array-element of the // Array over which we're iterating: return arr.filter(function (n) { // checking that there is one class-name, // and that the class-list contains the // passed-in class-name: return n.classList.length === 1 && n.classList.contains(cName); }); } } // finding the <input> elements in the document: var elements = document.querySelectorAll('input'), // converting the HTMLCollection into an Array, // using Array.prototype.slice(): elementsArray = Array.prototype.slice.call(elements, 0), // calling the function to find the elements with the // unique class name: uniquelyXYZ = uniquelyClassed(elementsArray, 'xyz'); // iterating over the Array using forEach(): uniquelyXYZ.forEach(function (el) { // adding the 'found' class-name to the // element (to visibly show the found elements): el.classList.add('found'); }); 并且没有奇怪的哈希码实现。