Первая и третья формы, которые вы показали, повторяют вызов GetNumber. Я предпочитаю вторую форму, хотя она имеет недостаток использования побочного эффекта в условиях, конечно. Однако я в значительной степени только делаю это с циклом while. Обычно я не заканчиваю тем, что передаю результат как аргумент - общие ситуации, в которых я нахожусь:
string line;
while ( (line = reader.ReadLine()) != null)
...
и
int bytesRead;
while ( (bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
...
Оба из них теперь настолько идиоматичны для меня, что не вызывают у меня никаких проблем - и, как я уже сказал, они позволяют мне изложить каждый кусочек логики только один раз.
Если вам не нравится переменная, имеющая слишком большую область видимости, вы можете просто ввести дополнительный блок:
{
int bytesRead;
while ( (bytesRead = stream.Read(buffer, 0, buffer.Length)) > 0)
{
// Body
}
}
Лично я не склонен это делать - «слишком широкий» охват меня не беспокоит , что сильно.
Я подозреваю, что не составит труда написать метод для инкапсуляции всего этого. Что-то вроде:
ForEach(() => reader.ReadLine(), // Way to obtain a value
line => line != null, // Condition
line =>
{
// body
};
Имейте в виду, для чтения строк у меня есть класс, который помогает:
foreach (string line in new LineReader(file))
{
// body
}
(Он не только работает с файлами - он довольно гибкий.)