为何VB.Net无法使用匿名类型执行GroupBy而C#可以?
为什么VB.NET使用匿名类型GroupBy无法正确分组,而C#可以?
这个问题的核心在于C#和VB.NET对匿名类型的相等性处理逻辑存在差异,我来给你拆解清楚:
1. 两者匿名类型的相等性实现差异
- 在C#中,编译器会自动为匿名类型重写
Equals()和GetHashCode()方法,比较逻辑是基于匿名类型的所有属性的值——只要两个匿名对象的所有属性值都相等,就会被判定为相等。这也是你的C#代码能正确分组的原因。 - 在VB.NET中,匿名类型默认是基于引用相等来比较的(和普通类一样),也就是说哪怕两个匿名对象的属性值完全一样,它们也是不同的实例,
Equals()会返回False。只有当你用Key关键字标记匿名类型的属性时,编译器才会生成基于属性值的相等性逻辑,和C#的行为一致。
2. 你的VB.NET代码问题所在
看你写的GroupBy分组键:
Function(g) New With { .Ean = g.Ean, .Size = g.Size }
这里的匿名类型没有用Key修饰属性,所以每个分组键都是一个新的匿名对象实例,GroupBy会把每个TestObjekt都当成独立的组,自然就得到了两个对象。
3. 修正后的VB.NET代码
只需要给匿名类型的属性加上Key关键字即可:
Sub Main() Dim objekts As New List(Of TestObjekt) objekts.Add(New TestObjekt() With {.Amount = 10, .Ean = 1, .Size = 2}) objekts.Add(New TestObjekt() With {.Amount = 100, .Ean = 1, .Size = 2}) ' 修正:用Key标记匿名类型的属性,启用值相等比较 Dim query = objekts.GroupBy( Function(g) New With { Key .Ean = g.Ean, Key .Size = g.Size }, Function(key, group) New TestObjekt() With { .Ean = key.Ean, .Size = key.Size, .Amount = group.Sum(Function(s) s.Amount) }).ToList() ' 现在会得到正确的1个分组对象 For Each q As TestObjekt In query Console.WriteLine("Ean: {0}, Size: {1}, Amount: {2}", q.Ean, q.Size, q.Amount) Next End Sub
4. 为什么字符串拼接的方法可行?
你用g.Ean.ToString() + "@" + g.Size.ToString()作为分组键,本质是用字符串的相等性来分组——字符串虽然是引用类型,但CLR为它重写了Equals()和GetHashCode(),相同内容的字符串会被判定为相等,所以能正确分组。不过这种方法有两个缺点:
- 性能不如匿名类型:字符串拼接会产生额外的内存开销,属性越多开销越大。
- 潜在的冲突风险:如果属性值本身包含你用来分隔的字符(比如这里的
@),可能会导致错误的分组(比如Ean=12,Size=@3 和 Ean=1,Size=2@3 会生成相同的字符串)。
所以还是用带Key的匿名类型更优雅、安全。
内容的提问来源于stack exchange,提问作者kara
相关产品推荐
相关产品推荐

