2013-04-25 100 views
4

我想了解linq如何工作。 我寫了一個測試應用程序,它不按我期望的方式工作。 從以下代碼,我期待看到項目「test1」和「test4」分組在一起,但我沒有得到。相反,我回來了4個獨立的團體。這意味着其中一個項目被分組在一起。 有人可以解釋我做錯了什麼? 謝謝。C#linq groupby返回不正確的組

public class linqtest 
{ public int x1; 
    public int x2; 
    public string x3; 

    public linqtest(int a, int b, string c) 
    { 
     x1 = a; 
     x2 = b; 
     x3 = c; 

    } 

    public bool Equals(linqtest other) 
    { 

     if (ReferenceEquals(null, other)) return false; 
     if (ReferenceEquals(this, other)) return true; 

     return x1 == other.x1 && 
       x2 == other.x2; 

    } 

    public override bool Equals(object obj) 
    { 
     if (ReferenceEquals(null, obj)) return false; 
     if (ReferenceEquals(this, obj)) return true; 
     if (obj.GetType() != typeof(linqtest)) return false; 
     return Equals((linqtest)obj); 
    } 
} 
linqtest tc14 = new linqtest(1, 4, "test1"); 
inqtest tc15 = new linqtest(3, 5, "test2"); 
linqtest tc16 = new linqtest(3, 6, "test3"); 
linqtest tc16a = new linqtest(1, 4, "test4"); 

List<linqtest> tclistitems = new List<linqtest>(); 
tclistitems.Add(tc14); 
tclistitems.Add(tc15); 
tclistitems.Add(tc16); 
tclistitems.Add(tc16a); 

IEnumerable<IGrouping<linqtest, linqtest>> tcgroup = tclistitems.GroupBy(c => c); 

爲什麼tcgroup包含4組?我期待3組。

+2

發帖前是否檢查過警告?我希望編譯器至少警告你,你不重寫GetHashCode。 – 2013-04-25 15:17:06

+0

謝謝。不,我沒有得到一個警告,我應該重寫GetHashCode。 – user2070073 2013-04-25 15:38:32

+0

你用什麼來編譯?我剛剛編譯了*精確*代碼並收到:「Test.cs(7,14):warning CS0659:'linqtest'覆蓋Object.Equals(object o),但不覆蓋Object.GetHashCode()。我會試着在其他方面解決這個問題 - 重要的是你可以看到警告。 – 2013-04-25 15:51:22

回答

6

發生錯誤是因爲您覆蓋Equals而不覆蓋GetHashCode。這兩個必須一起覆蓋,否則GroupBy將無法​​工作。

將此代碼添加到您的類來解決這個問題:

public override int GetHashCode() 
{ 
    // You are ignoring x3 for equality, so hash code must ignore it too 
    return 31*x1+x2; 
} 
+0

您使用31的任何原因? – 2013-04-25 15:34:13

+1

@JustinBicknell [是(這裏是鏈接)](http://stackoverflow.com/q/299304/335858)。 – dasblinkenlight 2013-04-25 15:42:19

2

因爲匿名類控制:對基於屬性,如struct你並不需要重寫Equal方法,只是利用匿名類:

tcgroup = tclistitems.GroupBy(c => new { c.x1, c.x2 }); 
+0

但它會在內存中創建不必要的對象。 – 2013-04-25 15:20:16

+0

@TimSchmelter:是的,當然,但我認爲在這種情況下性能微不足道,這段代碼更簡單,更具可讀性。 – 2013-04-25 15:22:29

+0

@Cuong_Le:但是已經有了自定義類,所以添加'GetHashCode'幾乎沒有開銷。它也很短,可讀和封裝。我只是想提到它,因爲大多數人不知道匿名類型會創造新的東西。 – 2013-04-25 15:28:25