Когда вы пишете функцию итератора в C #, используя синтаксис yield, компилятор внутренне преобразует вашу функцию в класс. Итак, если я напишу что-то вроде этого:
IEnumerator<int> MyIterator () {
for (int i = 0; i < 10; i++)
yield return i;
}
Вызов MyIterator () создает класс с закрытым полем для i вместо использования переменной стека.
Теперь к вопросу: я хочу найти способ найти поле, в котором хранится значение i. Причина немного запутана:
Я использую функции итератора для совместной работы с потоками. Я пишу простой код, подобный этому, в итераторе, чтобы приостановить выполнение функции до завершения асинхронной операции.
Future<string> f;
yield return file.readLine(out f);
string line = f.Result;
Когда эти три оператора выполняются, вызывается функция readLine, которая сохраняет будущее в поле 'f'. Затем функция итератора приостанавливается и возвращается в исходное состояние, когда в 'F' Future сохранен результат, и в этот момент я могу получить результат.
То, что я хочу сделать, это:
string line;
yield return file.readLine(out line);
При нормальных обстоятельствах это невозможно в .NET, поскольку нет способа гарантировать, что переменная стека останется в живых достаточно долго, чтобы я мог писать в нее.
Однако, зная, что функции итератора хранят все свои локальные объекты внутри полей, я теоретически могу сделать это, удерживая ссылку на итератор (поддерживая его) и на само поле, так что, когда операция readLine завершится, я смогу сохранить результат непосредственно в поле.
Проблема в том, что CLR, похоже, не дает мне способа сделать это.
Если у меня есть параметр 'out' или 'ref', я могу использовать небезопасный C # или необработанный MSIL для преобразования этого параметра в необработанный указатель, ссылку или TypedReference. К сожалению, не разрешено хранить любой из этих трех типов в поле - даже если вы обманываете компилятор, позволяя вам это сделать, верификатор байт-кода не позволит вам выполнить код. Это наиболее вероятно, потому что хранение ссылки на переменную стека было бы небезопасно.
Итак, я хочу написать функцию, которая принимает параметр out / ref и ищет поля вызывающего итератора, чтобы найти соответствующее поле. Как только у него есть поле, он сохраняет ссылку на итератор и поле в типе оболочки и связывает оболочку с асинхронной операцией.
Учитывая накладные расходы, связанные с отражением во время выполнения в .NET, я подозреваю, что это невозможно без предварительной обработки исходного кода или постобработки моих сборок после компиляции.
Однако я хотел бы услышать некоторые предложения для альтернативных решений. :) Есть идеи?