Нахождение поля объекта, соответствующего ссылочному параметру в C # - PullRequest
1 голос
/ 14 мая 2009

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

Однако я хотел бы услышать некоторые предложения для альтернативных решений. :) Есть идеи?

Ответы [ 2 ]

1 голос
/ 14 мая 2009

Обезьяна с полями итератора требует помощи. Не проще ли использовать член класса, который вы указали в сигнатуре итератора? Либо, передавая его внутрь или наружу? По существу, задействует какой-то простой класс-обертку?

class MyWrapper {
    public string Line {get;set;}
}

позволяет вам безопасно разговаривать с Line, просто через MyWrapper ссылку на объект?

Я не понял всего вопроса, так что это лучшее, что я могу сказать ...

0 голосов
/ 23 марта 2010

Я наконец-то нашел решение, которое отвечает моим потребностям здесь, и что вы знаете - это связано с LINQ.

В сообщении блога на .NET не упоминалось, как можно преобразовать лямбда-выражение в объект Expression <>, если вы знаете тип результата выражения. Учитывая это, получается, что вы можете взять выражение вроде этого:

() => this.Foo.Bar

И пройдитесь по узлам, чтобы получить MemberInfo (для 'Bar') и объект 'this' (для 'this.Foo'). Учитывая эти два значения, вы можете связать непосредственно с полем или свойством, указанным во время компиляции. В случае со свойством вы можете сделать это очень эффективно, извлекая методы Get и Set свойства из MemberInfo и преобразовывая их в строго типизированные делегаты, делая стоимость чтения / записи этого свойства очень низкой.

Лучше всего то, что это прекрасно работает с автоматическим рефакторингом и поиском исходного кода, потому что позволяет использовать возможности компилятора и не требует каких-либо изменений в существующем коде, чтобы разрешить связывание.

Единственным недостатком является то, что очень трудно привязать к полям / свойствам структуры, но я все равно не считаю это особенно уместным вариантом использования.

Любой, кто хочет решить эту или подобную проблему, может просмотреть исходный код здесь:

http://code.google.com/p/fracture/source/browse/trunk/Squared/Util/Bind.cs

...