2015-12-02 89 views
2

标志着这是因为重复的标题,请考虑以下短节目之前:如果比较两个序列相等

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) }; 
} 

我有一个返回类型为长的阵列序列的方法。为了测试它,我在Main内写了一些类似的测试代码。

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

对我来说,它看起来既是方法和epxected只包含一个单元素包含long类型的数组,不是吗?

编辑:那么我如何实现不具有异常含义来比较枚举中的元素返回平等?

+1

什么是例外? – Magnus

+0

我扔的那个? – HimBromBeere

+2

序列中的元素是'long []'。比较将是数组引用,它确实是不同的。数组中的元素(在序列中)将不会被比较。 –

回答

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

你的'GetHashCode'不正确。它可以很好地适用于这种特殊用途,因为'SequenceEqual'不使用'GetHashCode',但如果任何人使用'EqualityComparer'作为另一个'Distinct'使用'这将是不正确的。您需要生成一个与序列相等相关的哈希码。 –

+0

@JonHanna你是对的,这是一个简单的黑客攻击,只是为了表明可以轻松实现自定义比较器。我会解决它。 –

+0

几个月前我实现了一个泛型'EqualityComparer',但我可以使用lambda表达式来实现这两种方法。不过,我现在基本上使用这种方法,谢谢。 – HimBromBeere

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(); 

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

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本质上是做同样的事情。它检查每个枚举中每个元素的引用。

+0

Yeap,实际上是在一秒前发现的。然而,我的解决方案对我来说并不是很方便,你有更好的吗? – HimBromBeere

+0

@尤瓦尔的答案包含正确的方法。没有人指责我重写。 – Jamiec

相关问题