Использование регистра goto (переменная); в переключателе С#

Я пишу текстовое приключение для курса в консоли Visual Studio C# и решил использовать оператор switch вместо цепочки if-else из-за того, насколько полезным может быть случай goto (до сих пор он работал замечательно). Я знаю, что каждый случай внутри самого переключателя должен быть константой, но мне интересно, распространяется ли это и на использование случая перехода. Например, у меня есть:

switch (location)
{
case 1:
  break;
case 2:
  break;
case 3:
  break;
//I have 10 cases, each representing a location such as "Orc Cave", I just cut it down for brevity
default:
  break;
}

Я хотел бы иметь возможность вводить целочисленную переменную, а затем переходить к этому целому числу, для этого у меня есть следующее:

string travel2 = Console.ReadLine();//inputs a integer representing each location
int travel2A = Convert.ToInt32(travel2);
if (1<=travel2A && travel2A<=10)
{
    goto case(travel2A);
}
else{
    goto case(2);//current location
}

Все работает нормально, но есть предупреждение «Ожидается постоянное значение», подчеркивающее случай (travel2A). Можно ли сделать ввод goto case переменной с некоторыми настройками или это просто ограничение оператора switch? Если это последнее, я могу просто сделать цепочку if-else, но, на мой взгляд, более удобно вводить переменную. Любая помощь в этом очень ценится! Спасибо!


person Russell Dahlke    schedule 10.10.2013    source источник
comment
Вы можете только goto case, если вы находитесь внутри switch, чего нет во втором примере. Там у вас нет метки 2. См. msdn.microsoft.com/en-us/library/13940fs2.aspx, а затем сразу же см. cs.utexas.edu/~EWD /transscriptions/EWD02xx/EWD215.html и прекратите использовать goto.   -  person Preston Guillot    schedule 10.10.2013
comment
Тот факт, что вы не можете использовать goto, не означает, что вы не можете реализовать этот дизайн с помощью оператора switch. Смотрите мой ответ, чтобы обойти это.   -  person poy    schedule 10.10.2013
comment
Вы упомянули, что являетесь новичком в C#. Я предлагаю после того, как ваш код заработает, попробовать опубликовать его на codereview.stackexchange.com для получения конструктивной критики.   -  person Brian    schedule 10.10.2013


Ответы (5)


Метки для обычного goto должны быть указаны в исходном коде. Это не выражения, которые оцениваются; это просто идентификаторы. Метки для goto case должны быть постоянными выражениями, оцениваемыми во время компиляции, а не во время выполнения.

Я бы предостерег вас от любого решения, которое сильно зависит от "goto" в любой форме. Существует популярное предубеждение против любого вида «гото»; Gotos считаются неэлегантными и затрудняют понимание вашего кода. Безусловно, в этом предубеждении есть некоторая доля правды, хотя C# был тщательно разработан таким образом, что худшие злоупотребления "goto" невозможны или маловероятны. (C# разрешает переходы только внутри одного блока или из внутреннего блока в один из содержащих его блоков. Он никогда не допускает переход из внешнего блока во внутренний блок или между двумя блоками, которые не имеют отношения вложенности. Это значительно снижает вероятность «кода спагетти».)

Я предполагаю, что поскольку вы посещаете курс, вы только начинаете изучать C#. По мере продвижения вы узнаете о методах потока управления, которые более элегантны, чем включение случаев.

Пока мы критикуем ваш код: рассмотрите возможность использования int.TryParse вместо Convert.ToInt32 и убедитесь, что вы обрабатываете случай, когда пользователь вводит что-то, что не является целым числом.

person Eric Lippert    schedule 10.10.2013
comment
Спасибо! Это моя вторая неделя на C#, поэтому я все еще знакомлюсь с ним. Я переключусь на if-else для путешествий. Спасибо за рекомендацию int.TryParse. Я сделаю это прямо сейчас. - person Russell Dahlke; 10.10.2013
comment
@RussellDahlke: Пожалуйста. Похоже, вы хорошо думаете о потоке управления. Теперь сделайте шаг назад и спросите себя, можете ли вы осмыслить свой поток более абстрактно. Текстовая приключенческая игра по своей сути является машиной состояний. Конечные автоматы имеют следующий поток: (1) создать начальное состояние, (2) принять ввод, (3) отработать изменение состояния с учетом текущего состояния и ввода, (4) если новое состояние является остановкой состояние, завершить программу, (5) перейти к 2. Если вы сможете структурировать свою программу таким образом, ее будет легче понять. Очевидно, что (3) является сложной частью! - person Eric Lippert; 10.10.2013

См. ЭТО POST

В основном коммутаторы не могут иметь оценочные операторы в операторе case. Они должны быть статически оценены.

person Aditya Peshave    schedule 10.10.2013

Как насчет:

int travel2A = Convert.ToInt32(travel2);
if( travel2A < 1 || travel2A > 10 )
    travel2A = 2;

switch( travel2A ) { ... }
person Chris    schedule 10.10.2013

Ошибка компилятора A constant value is expected говорит сама за себя.

Причина, по которой это невозможно в C# (VB Select работает несколько иначе), хорошо видна при запуске в режиме отладки. Всякий раз, когда вы сталкиваетесь с оператором switch, вы заметите, что код перейдет к выбранному регистру. Хотя вы получаете значительный прирост производительности, пропуская несколько сравнений, это ограничивает гибкость.

Один из способов, которым я обошел это в прошлом, - это рекурсия с анонимными методами. Таким образом, вместо использования goto case(travel2a) вы должны использовать вызов метода, который повторно вызывает оператор switch.

Action<int> foo = null;
foo = (i) =>
{
    switch (i)
    {
        case 1:
            Console.WriteLine("1");
            break;
        case 2:
            Console.WriteLine("1");
            break;
        case 3:
            Console.WriteLine("1");
            break;
        default:
            //goto case (i%3);
            foo(i % 3);
            break;
    }
};

foo(4);
person poy    schedule 10.10.2013

Вы можете организовать case код как метод и просто вызвать его:

switch (location)
{
    case 1:
        gocave();
        break;
    case 2:
        gocave();
        break;
    case 3:
        donotgocave();
        break;
}

или разделить сложную логику на примитивы:

bool gocave = false;
bool eatmushroom = false;
switch (location)
{
    case 1:
        gocave = true;
        break;
    case 2:
        gocave = true;
        eathmushroom = true;
        break;
    case 3:
        break;
}
if(gocave) {...}
if(eatmushroom) {...}
person Sinatr    schedule 10.10.2013