2013-04-25 105 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