Почему мне нужно копировать "this" при использовании LINQ в структуре (и это нормально, если я это делаю)?
Легоки кода содержат простой запрос LINQ внутри неизменяемой структуры.
struct Point
{
static readonly List</*enum*/> NeighborIndexes;
//and other readonly fields!
public IEnumerable<FlatRhombPoint> GetEdges()
{
return from neighborIndex in NeighborIndexes;
select GetEdge(neighborIndex);
}
}
Он не компилируется.
Анонимные методы, лямбда-выражения и выражения запроса внутри structs не могут получить доступ к членам экземпляра 'this'. Рассмотрите возможность копирования 'this' для локальной переменной вне анонимного метода, лямбда выражение или выражение запроса и вместо этого используется локальная.
Кто-нибудь знает, почему это не разрешено?
Исправлено сообщение о том, что сообщение работает нормально:
public IEnumerable<FlatRhombPoint> GetEdges()
{
var thisCopy = this;
return from neighborIndex in NeighborIndexes;
select thisCopy.GetEdge(neighborIndex);
}
Но эта стандартная практика? Есть ли причины, по которым у таких запросов нет подобных запросов?
(В более крупной схеме вещей, делающих копию, меня не волнует как результат).
Ответы
Ответ 1
Методы экземпляров для structs вызываются со ссылкой на this
– скрытый параметр ref
.
Вот почему структурные методы способны мутировать структуры, на которые они вызывают.
Когда вы используете this
(или любую другую локальную переменную/параметр) внутри выражения лямбда или запроса LINQ, компилятор превращает его в поле на класс закрытия компилятора.
CLR не поддерживает поля ref
, поэтому захваченный this
будет работать так же, как обычный this
. (это также является причиной того, что вы не можете использовать параметры ref
внутри lambdas)
Итерационные методы имеют одинаковую проблему: ndash; они скомпилированы в скрытый класс перечислителя, и все переменные или параметры становятся полями в классе (поэтому итераторы не могут принимать параметры ref
).
Однако для итераторов С# приняла противоположное решение. Внутри итератора вы можете использовать this
, но он будет скопирован в поле класса перечислителя.
Это означает, что если вы мутируете структуру внутри итератора, мутации не будут выполняться с копией вызывающего абонента.