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