2017-01-01 6 views
0

В настоящее время я пытаюсь определить две целые переменные с помощью ввода пользователем. Если пользователь вводит целое число, которое не содержится в определенном диапазоне, пользователю будет предложено ввести его снова, пока пользователь не введет принятое значение.Доступ к локальной переменной внутри цикла while #

static void Main(string[] args) 
{ 
    int a; 
    int b; 

    bool isAValid = false; 
    bool isBValid = false; 

    while (isAValid == false) 
    { 
     Console.Write("Input number from 2 to 20: "); 
     isAValid = true; 

     a = Convert.ToInt16(Console.ReadLine()); 

     if (a < 2 || a > 20) 
     { 
      isAValid = false; 
     } 
    } 

    while (isBValid == false) 
    { 
     Console.Write("Input number from 2 to 20: "); 
     isBValid = true; 

     b = Convert.ToInt16(Console.ReadLine()); 

     if (b < 2 || b > 20) 
     { 
      isBValid = false; 
     } 
    } 
} 

То, что я пытаюсь сделать, это сохранение пользовательского ввода в в и б соответственно, так что локальная переменная величина определяется как значение пользовательского ввода. Тем не менее, когда я пытаюсь получить доступ к локальной переменной после того, как последний цикл был сломан, я получаю «Использование неназначенной локальной переменной a (или b)».

Любые идеи о том, как я могу определить переменные, все еще имея цикл ввода ввода?

+1

Просьба показать заявление, на которое указывает ошибка. –

+2

Существует буквально сотни сообщений на этом сайте с заголовком «Использование неназначенной локальной переменной». Прочтите их и поймите это сообщение об ошибке. –

+0

@ Eric Program flow afaics гарантирует, что они будут назначены (если исключение не будет вызвано конверсией); Я полагаю, что то, что озадачивает OP, заключается в том, что компилятор не может видеть, что мы видим: что циклы while всегда вводятся, и поэтому всегда выполняются вход и назначение. Интересно, поможет ли компилятор (более подходящий) цикл do-while. –

ответ

0

Чтобы исправить ошибку, объявляю:

int a = 0; 
int b = 0; 

Вместо:

int a; 
int b; 

Это будет инициализировать локальные переменные и зафиксирует ваше сообщение об ошибке.

+0

Это только поверхностное исправление: если действительно возможно как-то обойти назначение от ввода, значение по умолчанию будет неотличимо от возможного ввода, а условие ошибки останется незамеченным. Программа должна обеспечить, чтобы выполнялось задание или выполнялась ошибка. Если это действительно возможно, чтобы обойти вход, программа должна выразить это, а не сгладить его избыточной инициализацией. –

1

Забавно, что компилятор не может видеть, что петли while всегда вводятся, и a и b всегда инициализируются.

Обновление: Эрик Липперт отметил, что для анализа управления потоком все выражения с переменными считаются возможными истинными или ложными; даже такие выражения, как x*0 == 0.

We видеть, что условие while всегда верно, отчасти потому, что мы знаем этот небольшой логический шаблон для ввода. Мы знаем, что такое цикл. Компилятор нет. Он видит произвольное условие и не может быть обеспокоен тем, что он всегда верен. Он просто считает все выражения с переменными непостоянными.

Один из способов исправить это, чтобы выразить логику программы лучше на языке. Такая картина не является чем-то необычным: «Повторять что-то до тех пор, пока условие не будет удовлетворено». В частности, делают это хотя бы один раз в любом случае. Существует языковая конструкция для всех языков семейства C: цикл do/while. Переписав петлю для a дает

do 
{ 
    Console.Write("Input number from 2 to 20: "); 
    isAValid = true; 

    a = Convert.ToInt16(Console.ReadLine()); 

    if (a < 2 || a > 20) 
    { 
     isAValid = false; 
    } 
} while (isAValid == false); 

Теперь я могу получить доступ к a после цикла; компилятор признает, что назначение будет гарантировано. То же самое должно работать для b.

Это также позволяет нам писать менее загроможденный код. Вспомогательная переменная isAValid необходима только из-за «искусственного» теста, когда первый цикл цикла введен первым. Тест является искусственным, потому что у нас пока нет ввода и, следовательно, он не может ничего проверить. Если мы действительно проверим только после ввода, чтобы тестовые данные были доступны, мы можем поставить тест прямо в состояние.Он сводится к

do 
{ 
    Console.Write("Input number from 2 to 20: "); 
    a = Convert.ToInt16(Console.ReadLine()); 

} while (a < 2 || a > 20); 
+0

Не то, чтобы команда компилятора не могла беспокоиться о работе; скорее, разработчики языка предпочтут четкое, простое правило: все условия, которые включают переменные, считаются возможными истинными или, возможно, ложными для анализа потока управления. Да, это слишком близко к достижимости, но мы * имеем * чрезмерно приблизить достижимость, если мы хотим, чтобы компилятор завершил работу. Строка должна быть нарисована * где-то *, и правильным решением является выбор простой, понятной и простой для объяснения позиции для этой строки. –

+0

@EricLippert Привет, я сказал «компилятор», а не «компилятор * команда *» ;-). Просто мы привыкли к удивительным возможностям компилятора вывода, которые иногда не очевидны для случайного наблюдателя (Linq). И тогда у нас есть 'b = false; if (b) ... 'и компилятор не работает. Конечно, линия должна быть где-то. С другой стороны, я не удивлюсь, если компилятор полностью выбросит сравнимое условие if if в * optimizing pass * позже (после того, как он проверит правильность). Но тогда пользователь указал, что ожидание немного для оптимизации в порядке. –

+0

На самом деле я удалил ошибку в этой области. В C# 2.0 арифметический оптимизатор запускался до определенной проверки правильности, что неверно. 'int x; int y = 1; if (y * 0! = 0) F (x); 'рассматривается как' int x; int y = 1; if (false) F (x); 'который является законным, но' y * 0! = 0' включает переменную, а по спецификации следует рассматривать как возможно true. C# 3.0 выполняет арифметическую оптимизацию после определенной проверки назначения. –

Смежные вопросы