VB.NET linq group by匿名类型不能按预期工作

mat*_*mc3 11 c# linq vb.net immutability linqpad

我正在玩弄LINQPad附带的一些linq样本.在"Nutshell中的C#3.0"文件夹中,在Chater 9 - Grouping下,有一个名为"按多个键分组"的示例查询.它包含以下查询:

from n in new[] { "Tom", "Dick", "Harry", "Mary", "Jay" }.AsQueryable()
group n by new
{
    FirstLetter = n[0],
    Length = n.Length
}
Run Code Online (Sandbox Code Playgroud)

我将字符串"Jon"添加到数组的末尾以获得实际的分组,并得出以下结果:

C#LINQPad结果

这正是我所期待的.然后,在LINQPad中,我去了相同查询的VB.NET版本:

' Manually added "Jon"
from n in new string() { "Tom", "Dick", "Harry", "Mary", "Jay", "Jon" }.AsQueryable() _
group by ng = new with _
{ _
    .FirstLetter = n(0), _
    .Length = n.Length _
} into group
Run Code Online (Sandbox Code Playgroud)

结果没有正确地将Jay/Jon组合在一起.

VB.NET LINQPad结果

把我的头发拉了一下之后,我发现这篇MSDN文章讨论了VB.NET的匿名类型.在VB.NET中,默认情况下它们是可变的,而不是C#,它们是不可变的.在VB中,您需要添加Key关键字以使它们不可变.所以,我将查询更改为此(注意添加Key):

from n in new string() { "Tom", "Dick", "Harry", "Mary", "Jay", "Jon" }.AsQueryable() _
group by ng = new with _
{ _
    Key .FirstLetter = n(0), _
    Key .Length = n.Length _
} into group
Run Code Online (Sandbox Code Playgroud)

这给了我正确的结果:

在此输入图像描述

所以我的问题是:

  1. 当linq进行相等比较时,为什么匿名类型的可变性/不变性很重要?值得注意的是,在Linq-to-SQL中它根本不重要,这可能只是SQL转换的产物.但是在Linq-to-objects中,它显然有所不同.
  2. 为什么MS选择让VB的匿名类型变为可变.我认为没有真正的优势,在解决了这个问题后,我发现了一些非常现实的缺点.即你的linq查询可能有微妙的错误.

- 编辑 -

只是一个有趣的额外信息...显然这是关键的财产问题是众所周知的.我只是不知道谷歌要做什么.它已经讨论了在这里这里的计算器.这是使用匿名类型和Distinct的另一个问题示例:

Dim items = New String() {"a", "b", "b", "c", "c", "c"}
Dim result = items.Select(Function(x) New With {.MyValue = x}).Distinct()
Dim result2 = items.Select(Function(x) New With {Key .MyValue = x}).Distinct()
'Debug.Assert(result.Count() = 3) ' Nope... it's 6!
Debug.Assert(result2.Count() = 3)
Run Code Online (Sandbox Code Playgroud)

Jon*_*eet 11

Key修改不只是影响的可变性-它也影响的行为EqualsGetHashCode.Key这些计算中只包含属性...这显然会影响分组等.

至于为什么它与VB不同 - 我不知道.这对我来说似乎很奇怪.我知道我很高兴C#以它的方式工作但是:)即使可以认为使属性可选地变为有意义,我也不明白为什么它应该是默认值.