比较两个序列是否相等
Compare if two sequences are equal
在因为标题将其标记为重复之前,请考虑以下短程序:
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) };
}
我有一个方法 return 是一个 long 类型的数组序列。为了测试它,我写了一些 test-code 类似于 Main
中的那个。
但是我得到了异常,但我不知道为什么。预期的序列不应该与实际 returned 的序列相媲美,还是我错过了什么?
在我看来,方法和 epxected
都只包含一个包含 long 类型数组的元素,不是吗?
编辑:那么我如何实现不获得将枚举中的元素与 return 相等性进行比较的异常含义?
SequenceEquals
测试序列中的元素是否相同。枚举中的元素属于 long[]
类型,因此我们实际上是将两个不同的数组(但包含相同的元素)相互比较,这是通过比较它们的引用而不是它们的实际值来完成的。
所以我们在这里实际检查的是 expected[0] == actual[0]
而不是 expected[0].SequqnceEquals(actual[0])
这很奇怪 returns 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
也不需要奇怪的哈希码实现。
不,你的序列不相等!
让我们删除序列位,只取每个项目第一个元素中的内容
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
的代码本质上是在做同样的事情。它检查可枚举中每个元素在每种情况下的引用。
实际问题是您正在比较两个 long[]
,而 Enumerable.SequenceEquals
将使用 ObjectEqualityComparer<Int64[]>
(您可以通过检查 EqualityComparer<long[]>.Default
看到是 Enumerable.SequenceEquals
) 内部使用的内容,它将比较这两个数组的引用,而不是存储在数组中的实际 values,这显然是不一样的.
要解决这个问题,您可以编写自定义 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
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;
}
}
}
在因为标题将其标记为重复之前,请考虑以下短程序:
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) };
}
我有一个方法 return 是一个 long 类型的数组序列。为了测试它,我写了一些 test-code 类似于 Main
中的那个。
但是我得到了异常,但我不知道为什么。预期的序列不应该与实际 returned 的序列相媲美,还是我错过了什么?
在我看来,方法和 epxected
都只包含一个包含 long 类型数组的元素,不是吗?
编辑:那么我如何实现不获得将枚举中的元素与 return 相等性进行比较的异常含义?
SequenceEquals
测试序列中的元素是否相同。枚举中的元素属于 long[]
类型,因此我们实际上是将两个不同的数组(但包含相同的元素)相互比较,这是通过比较它们的引用而不是它们的实际值来完成的。
所以我们在这里实际检查的是 expected[0] == actual[0]
而不是 expected[0].SequqnceEquals(actual[0])
这很奇怪 returns 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
也不需要奇怪的哈希码实现。
不,你的序列不相等!
让我们删除序列位,只取每个项目第一个元素中的内容
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
的代码本质上是在做同样的事情。它检查可枚举中每个元素在每种情况下的引用。
实际问题是您正在比较两个 long[]
,而 Enumerable.SequenceEquals
将使用 ObjectEqualityComparer<Int64[]>
(您可以通过检查 EqualityComparer<long[]>.Default
看到是 Enumerable.SequenceEquals
) 内部使用的内容,它将比较这两个数组的引用,而不是存储在数组中的实际 values,这显然是不一样的.
要解决这个问题,您可以编写自定义 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
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;
}
}
}