Я играл с некоторыми образцами 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" в конец массива, чтобы получить фактическую группировку, и придумал следующий результат:
Именно это я и ожидал. Затем в 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.
Потянув за волосы немного, я обнаружил эту статью 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
Это дало мне правильный результат:
Итак, мой вопрос:
- Почему изменчивость/неизменность анонимных типов имеет значение, когда 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)