带有匿名类型的 VB.NET linq group by 未按预期工作

发布于 2024-11-24 12:35:16 字数 2321 浏览 4 评论 0 原文

我正在研究 LINQPad 附带的一些 linq 示例。在“C# 3.0 in a Nutshell”文件夹中的 Chater 9 - Grouping 下,有一个名为“Grouping by Multiple Keys”的示例查询。它包含以下查询:

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

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

C# LINQPad result

这正是我所期待的。然后,在 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

结果没有正确地将 Jay/Jon 分组在一起。

VB.NET LINQPad result

在费了一番功夫后,我发现 这篇 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

这给了我正确的结果:

在此处输入图像描述

所以我的问题是:

  1. 为什么当 linq 进行相等比较时,匿名类型的可变性/不可变性很重要?值得注意的是,在 Linq-to-SQL 中,这根本不重要,这可能只是转换为 SQL 的产物。但在 Linq-to-objects 中,它显然具有重要意义。
  2. 为什么 MS 选择使 VB 的匿名类型可变。我没有看到真正的优势,在研究了这个问题之后,我看到了一些非常现实的缺点。也就是说,您的 linq 查询可能存在细微的错误。

-- 编辑 --

只是一个有趣的额外信息......显然这是众所周知的键控财产问题。我只是不知道谷歌有什么用。 此处这里在 stackoverflow 上。这是使用匿名类型和 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)

I was toying around with some of the linq samples that come with LINQPad. In the "C# 3.0 in a Nutshell" folder, under Chater 9 - Grouping, there is a sample query called "Grouping by Multiple Keys". It contains the following query:

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

I added the string "Jon" to the end of the array to get an actual grouping, and came up with the following result:

C# LINQPad result

This was exactly what I was expecting. Then, in LINQPad, I went to the VB.NET version of the same query:

' 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

The result does not properly group Jay/Jon together.

VB.NET LINQPad result

After pulling my hair out for a bit, I discovered this MSDN article discussing VB.NET anonymous types. In VB.NET they are mutable by default as opposed to C# where they are immutable. In VB, you need to add the Key keyword to make them immutable. So, I changed the query to this (notice the addition of 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

This gave me the correct result:

enter image description here

So my question is this:

  1. Why does mutability/immutability of anonymous types matter when linq does an equality comparison? Notably, in Linq-to-SQL it doesn't matter at all, which is likely just a product of the translation to SQL. But in Linq-to-objects it apparently makes all the difference.
  2. Why would MS have chosen to make VB's anonymous types mutable. I see no real advantage, and after mucking around with this issue I see some very real disadvantages. Namely that your linq queries can have subtle bugs.

-- EDIT --

Just an interesting extra piece of info... Apparently this is keyed property issue is widely known. I just didn't know what to Google for. It's been discussed here and here on stackoverflow. Here's another example of the issue using anonymous types and 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)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

破晓 2024-12-01 12:35:16

Key 修饰符不仅影响可变性 - 它还影响 EqualsGetHashCode 的行为。这些计算中只包含Key属性...这显然会影响分组等。

至于为什么它对于VB不同 - 我不知道。我也觉得很奇怪。我知道很高兴C#按照它的方式工作:)即使可以说使属性可选可变是有意义的,我不明白为什么它应该是默认的。

The Key modifier doesn't just affect mutability - it also affects the behaviour of Equals and GetHashCode. Only Key properties are included in those calculations... which clearly affects grouping etc.

As for why it's different for VB - I don't know. It seems odd to me too. I know I'm glad that C# works the way it does though :) Even if it could be argued that making properties optionally mutable makes sense, I don't see why it should be the default.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文