2015-12-02 136 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

相關問題