C # как использовать enum с переключателем

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

public enum Operator
{
    PLUS, MINUS, MULTIPLY, DIVIDE
}

public double Calculate(int left, int right, Operator op)
{

    int i = (int) op;

    switch(i)
    {
        case 0:
        {
            return left + right;
        }

        case 1:
        {
            return left - right;
        }

        case 2:
        { 
            return left * right;
        }

        case 3:
        {
            return left / right;
        }

        default:
        {
            return 0.0;
        }
    }
}

Конечный результат должен быть примерно таким:

Console.WriteLine("The sum of 5 and 5 is " + Calculate(5, 5, PLUS))
Output: The sum of 5 and 5 is 10

Не могли бы вы, ребята, рассказать мне, в чем я ошибаюсь?


person yesman    schedule 28.02.2013    source источник


Ответы (10)


Вам не нужно его конвертировать

switch(op)
{
     case Operator.PLUS:
     {
        // your code 
        // for plus operator
        break;
     }
     case Operator.MULTIPLY:
     {
        // your code 
        // for MULTIPLY operator
        break;
     }
     default: break;
}

Кстати, используйте скобки

person J.Starkl    schedule 28.02.2013
comment
@ J.Starkl Ну, я думаю, это вопрос личного мнения :-) - person Stephan Bauer; 28.02.2013
comment
@StephanBauer: Да, это так :-) - person J.Starkl; 28.02.2013
comment
Я, скорее, человек с отступом =) Как ни странно, это единственное, на что я не ставлю подтяжки. Иди разберись! - person Kenneth K.; 28.02.2013
comment
Если вы когда-либо вводите новую переменную для использования в одном случае, вам, конечно, нужно использовать скобки. И тут срабатывает мое ОКР, и мне нужно сделать все случаи одинаковыми, добавив к ним скобки. (Если серьезно, я использую скобки в операторах switch, потому что я использую их для блоков повсюду.) - person Matthew Watson; 28.02.2013
comment
@MatthewWatson Отлично! Спасибо, что упомянули этот момент с variabloes для одного случая - никогда не думал об этом :-) - person Stephan Bauer; 01.03.2013
comment
В C # могут быть многострочные блоки без скобок, поскольку все они должны заканчиваться возвратом или разрывом, переходом или вообще не иметь кода. Падение не допускается за пределами этих параметров: http://stackoverflow.com/questions/174155/switch-statement-fallthrough-in-c - person Rafe; 29.04.2014
comment
Да, но если вы используете их повсюду -нет подводных камней -согласованный код -повышенная читаемость -простое обслуживание / расширение -лучшая практика (привыкните к ним) - person J.Starkl; 30.04.2014
comment
о читабельности - я считаю, что код Python и haskell легче читать, чем C # или java - person IARI; 03.06.2016
comment
Обратите внимание, что скобки не относятся к переключателю / регистру: вы можете добавить скобки в любом месте, где хотите разделить переменные. - person Matthieu Charbonnier; 26.11.2017
comment
Переключить регистр позволяет повторно использовать один и тот же блок кода для нескольких случаев. Если да - не используйте скобки. - person Nadav; 12.10.2019
comment
Я думаю, что это немного странно, что вы используете фигурные скобки для каждого случая (чтобы они соответствовали), но ... не по умолчанию: ... черт возьми, по умолчанию даже в той же строке, а не в других случаях. Не то чтобы критиковать, а просто наслаждаться тем, как разные люди по-разному определяют согласованность. - person tekHedd; 25.03.2020

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

public double Calculate(int left, int right, Operator op) =>
            op switch 
        {
            Operator.PLUS => left + right,
            Operator.MINUS => left - right,
            Operator.MULTIPLY => left * right,
            Operator.DIVIDE => left / right,
            _    =>  0
        }

Ref. https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-8

person Przemek Struciński    schedule 09.10.2019
comment
Подойдет любой синтаксический сахар, который снижает потребность в фигурных скобках. - person 0b101010; 26.03.2021

Правильный ответ уже дан, но вот способ лучше (чем переключение):

private Dictionary<Operator, Func<int, int, double>> operators =
    new Dictionary<Operator, Func<int, int, double>>
    {
        { Operator.PLUS, ( a, b ) => a + b },
        { Operator.MINUS, ( a, b ) => a - b },
        { Operator.MULTIPLY, ( a, b ) => a * b },
        { Operator.DIVIDE ( a, b ) => (double)a / b },
    };

public double Calculate( int left, int right, Operator op )
{
    return operators.ContainsKey( op ) ? operators[ op ]( left, right ) : 0.0;
}
person JustAndrei    schedule 28.02.2013
comment
Я думаю, что это пример того, где было бы уместно использовать var для объявления переменной! - person Chris Dunaway; 28.02.2013
comment
Var нельзя использовать вне тела метода. - person JustAndrei; 01.03.2013
comment
О да, я пропустил, что это было объявлено вне метода. - person Chris Dunaway; 01.03.2013
comment
Вы можете использовать using OperatorsDict = Dictionary<Operator, Func<int, int, double>>, а затем вместо этого использовать OperatorsDict - person Adassko; 15.10.2013
comment
@JustAndrei - (я знаю, что это старый пост) Почему этот метод лучше, чем переключение - он быстрее? или это предпочтение? Я хотел бы знать не для аргументов, а для лучшего кодирования - я всегда использовал переключатель, но никогда не думал использовать функцию словаря. Конечно, не должно быть в сериализуемом объекте ... конечно, комментарий Адашко заставляет мои мысли о проблеме с памятью уйти. - person Stix; 20.11.2015
comment
1. Разделение конфигурации и логики: ваш код становится коротким и легко читаемым. Ваша конфигурация читается как таблица, что тоже легко. 2. В отличие от switch, Dictionary внутренне использует хеш-таблицу для быстрого поиска, что становится важным при большом количестве случаев. Иногда программисты помещают наиболее часто используемый элемент в конец переключателя и возникают проблемы с производительностью. 3. Как только конфигурация извлечена из кода, теперь вы можете делать много замечательных вещей: - person JustAndrei; 22.11.2015
comment
A. Переместите его из кода во внешний источник, например файл конфигурации или база данных. Б. Динамически изменяйте конфигурацию в соответствии с вашими потребностями прямо во время выполнения программы. C. Создайте систему плагинов, чтобы ваша конфигурация была расширена модулями. D. Хотя Словарь уже помогает быстрее искать ключ, вы можете сделать его еще лучше, настроив способ поиска операции. Однажды я реализовал такую ​​замену переключателя, который накапливал статистику использования кейсов во время выполнения программы и периодически перестраивал список кейсов так, чтобы самый популярный становился первым. - person JustAndrei; 22.11.2015
comment
Я хотел бы увидеть одно надежное свидетельство того, что поиск по словарю выполняется быстрее, чем «Switch with Enums». - person BillW; 31.03.2016
comment
@BillW Нет, потому что это не так. Использование словаря для этого - просто ужасный выбор дизайна. - person 0b101010; 26.03.2021

Вы не должны приводить к целому числу. И для деления вам нужно сначала преобразовать left в double, в противном случае вы будете делать целочисленное деление.

public enum Operator
{
    PLUS, MINUS, MULTIPLY, DIVIDE
}

public double Calculate(int left, int right, Operator op)
{
    double sum = 0.0;

    switch(op)
    {
       case Operator.PLUS:
       sum = left + right;
       return sum;

       case Operator.MINUS:
       sum = left - right;
       return sum;

       case Operator.MULTIPLY:
       sum = left * right;
       return sum;

       case Operator.DIVIDE:
       sum = (double)left / right;
       return sum;

       default:
       return sum;
   }

   return sum;
}
person Jeow Li Huan    schedule 28.02.2013

просто не приводите к int

 switch(operator)
    {
       case Operator.Plus:
       //todo
person burning_LEGION    schedule 28.02.2013

Конвертировать не нужно. Вы можете применять условия к перечислениям внутри переключателя. Вот так,

public enum Operator
{ 
    PLUS,
    MINUS,
    MULTIPLY,
    DIVIDE
}

public double Calculate(int left, int right, Operator op)
{
    switch (op)
    {
        case Operator.PLUS: return left + right; 
        case Operator.MINUS: return left - right; 
        case Operator.MULTIPLY: return left * right;
        case Operator.DIVIDE: return left / right;
        default: return 0.0; 
    }
}

Затем назовите это так:

Console.WriteLine("The sum of 5 and 5 is " + Calculate(5, 5, Operator.PLUS));
person Kent Aguilar    schedule 11.01.2017
comment
Этот ответ прекрасно иллюстрирует подходящее время для нарушения соглашения и использования 1) раннего возврата из функции и 2) помещения случая в ту же строку, что и код. - person tekHedd; 25.03.2020

Если вы не хотите использовать оператор return для каждого случая, попробуйте следующее:

Calculate(int left, int right, Operator op)
{
   int result = 0;
   switch(op)
   {
        case Operator.PLUS:
        {
            result = left + right;;  
        }
        break;
        ....
   }

   return result;
}
person Martin    schedule 03.10.2014
comment
Непонятно, как это делает код более читабельным, кроме как для обеспечения соблюдения соглашений о локальном стиле проекта. Примерный блок кода OP - классический пример того, как ранний возврат может сделать код более читабельным. - person tekHedd; 25.03.2020

Все остальные ответы верны, но вам также необходимо правильно вызвать свой метод:

Calculate(5, 5, Operator.PLUS))

И поскольку вы используете int для left и right, результат также будет int (3/2 will result in 1). вы можете преобразовать его в double перед вычислением результата или изменить свои параметры, чтобы принять double

person Stephan Bauer    schedule 28.02.2013
comment
Ну .. да, вроде ... :-D - person Stephan Bauer; 28.02.2013

Две вещи. Во-первых, вам необходимо указать ссылку на перечисление в вашем тесте - вместо «PLUS», это должно быть «Operator.PLUS». Во-вторых, этот код был бы намного более читабельным, если бы вы использовали имена членов перечисления, а не их целые значения в операторе switch. Я обновил ваш код:

public enum Operator
{
    PLUS, MINUS, MULTIPLY, DIVIDE
}

public static double Calculate(int left, int right, Operator op)
{
    switch (op)
    {
        default:
        case Operator.PLUS:
            return left + right;

        case Operator.MINUS:
            return left - right;

        case Operator.MULTIPLY:
            return left * right;

        case Operator.DIVIDE:
            return left / right;
    }
}

Назовите это с помощью:

Console.WriteLine("The sum of 5 and 5 is " + Calculate(5, 5, Operator.PLUS));
person Mike    schedule 28.02.2013

Ваш код в порядке. Если вы не знаете, как использовать функцию вычисления, попробуйте

Calculate(5,5,(Operator)0); //this will add 5,5
Calculate(5,5,Operator.PLUS);// alternate

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

public enum Operator{PLUS=21,MINUS=345,MULTIPLY=98,DIVIDE=100};
person novic3    schedule 28.02.2013