VB.NET linq group с анонимными типами, которые не работают должным образом

Я играл с некоторыми образцами linq, которые поставляются с LINQPad. В папке "С# 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
}

Я добавил строку "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 они изменяются по умолчанию, а не С#, где они неизменяемы. В 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

Это дало мне правильный результат:

enter image description here

Итак, мой вопрос:

  • Почему изменчивость/неизменность анонимных типов имеет значение, когда linq выполняет сравнение равенства? Примечательно, что в Linq-to-SQL это вообще не имеет значения, что, скорее всего, является результатом перевода на SQL. Но в Linq-to-objects это, по-видимому, имеет значение.
  • Почему MS предпочла бы модифицировать анонимные типы VB. Я не вижу никакого реального преимущества, и, после того, как я обрушился с этой проблемой, я вижу некоторые очень серьезные недостатки. А именно, что ваши запросы linq могут иметь тонкие ошибки.

- EDIT -

Просто интересная дополнительная информация... Видимо, это ключевая проблема собственности широко известна. Я просто не знал, что для Google. Здесь обсуждалось и здесь в 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 6!
Debug.Assert(result2.Count() = 3)

Ответ 1

Модификатор Key влияет не только на изменчивость - он также влияет на поведение Equals и GetHashCode. В эти вычисления включены только свойства Key, которые явно влияют на группировку и т.д.

Что касается того, почему это отличается от VB - я не знаю. Мне кажется странным. Я знаю, что я рад, что С# работает так, как он это делает:) Даже если можно утверждать, что внесение свойств, опционально изменяемых, имеет смысл, я не понимаю, почему это должно быть по умолчанию.