Список действий/функций C#

У меня есть программа, которая должна выполнять функцию в соответствии с Enum, и мне интересно, есть ли другой способ, кроме этого:

enum FunctionType
{
 Addition = 0,
 Substraction = 1,
 Mutiplication = 2,
 Division = 3
}
void ExecuteFunction(FunctionType Function)
{
  switch(Function)
  {
    case FunctionType.Addition: Addition();
    break;
    case FunctionType.Substraction: Subsctration();
    break;      
    ...
    default: ...  
  }
}

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

enum FunctionType : Action
{
 Addition = new Action(Addition);
 Substraction = new Action(Substraction);
 ....
}
void ExecuteFunction(FunctionType Function)
{
 (Action)Function.Invoke();
}

Переключатель не нужен, и то, что может быть 50 строк, превращается в 1 линию. Но это невозможно сделать, в качестве перечислений принимаются только числовые типы. Я думаю, что можно иметь List<T> действий, но для этого потребуется добавить каждое действие в список во время выполнения.

РЕДАКТИРОВАТЬ: я нашел в исходном коде способ, которым это делается, но я не могу этого понять. Вот что я получаю: они создают пользовательский Attribute, который содержит string (имя метода) и методы, которые они делают:

[CustomAtrribute("name")]
void Method()
{    
}

Тогда я не знаю, как это называется по имени, я предполагаю, что это какое-то отражение, но я не знаю, как найти информацию об этом.

EDIT2: я нашел способ сделать это, я добавлю интерфейс с функцией, затем реализую этот интерфейс с кодом внутри функции и использую Dictionary<Enum, Interface> для его вызова. Я не знаю, должен ли я отвечать на свой вопрос, в любом случае, спасибо всем, кто мне помог.


person Pau C    schedule 19.08.2016    source источник
comment
Ну, у вас может быть Dictionary<FunctionType, Action>... но опять же, вам нужно будет создать его во время выполнения. Если все имена методов совпадают с именами значений перечисления, вы можете сделать это с помощью отражения.   -  person Jon Skeet    schedule 19.08.2016
comment
Вы можете запустить действие, просто добавив (), не нужно делать .Invoke(). Просто к вашему сведению   -  person vrwim    schedule 19.08.2016
comment
@Jon Skeet Может быть, словарь работает нормально, но добавление всего звучит не очень хорошо. Все же лучше, чем гигантский переключатель. Приведение каждого имени метода в соответствие с каждым именем перечисления звучит немного опасно при добавлении новых вещей.   -  person Pau C    schedule 19.08.2016
comment
Конечно, вы могли бы добавить для него модульный тест. В конце концов, вы не сказали нам, почему у вас есть перечисление для начала. Почему ваш метод ExecuteFunction не может просто взять Action или что-то в этом роде?   -  person Jon Skeet    schedule 19.08.2016
comment
Я хочу сделать это на сервере, и каждое значение в перечислении соответствует другому типу пакета, каждый пакет идентифицируется значением int и анализируется в Enum. С несколькими различными типами пакетов коммутатор был бы в порядке, но когда я обновляю программу, я хочу добавить больше пакетов, и я хочу, чтобы код был чистым, а не переключателем 50 case.   -  person Pau C    schedule 19.08.2016
comment
Осторожно, синтаксический анализ перечислений может быть сложным: stackoverflow.com/questions/28219215/   -  person argyle    schedule 19.08.2016
comment
Я просто привожу их к типу int и сначала проверяю, существует ли значение. Но спасибо.   -  person Pau C    schedule 19.08.2016


Ответы (2)


Не могу сказать, что я бы рекомендовал это сделать, но:

public static class Functions
{
    public static Func<int, int, int> Add = (x, y) => { return x + y; };
}

Тогда вы просто позвоните Functions.Add(1,1)

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

public static class Functions
{
    public static void Add()
    {
        Debug.Print("Add");
    }

    public static void Subtract()
    {
        Debug.Print("Subtract");
    }

    public enum Function
    {
        Add,
        Subtract
    }

    public static void Execute(Function function)
    {
        typeof(Functions).GetMethod(function.ToString()).Invoke(null, null);
    }
}

Затем Functions.Execute(Functions.Function.Add) (дополнительные функции, потому что мое перечисление было внутри класса функций).

person Dave Williams    schedule 19.08.2016
comment
Это выглядит интересно, но повлияет ли это на производительность при многократном вызове? И почему .Invoke(null, null);? - person Pau C; 19.08.2016
comment
Я проверил это, и влияние на производительность довольно велико. Требуется x30, если я ввожу функцию имени в виде строки, и x133, если я делаю .ToString() по сравнению с обычным вызовом функции. Если я сначала загружу methodInfo, это займет время x20. - person Pau C; 19.08.2016
comment
@null .Invoke(null,null) для вызова статического метода без аргументов, возможно, вам придется изменить его для своей задачи. Производительность с использованием отражения сильно пострадает. Я сказал, что предлагаю вам не делать этого. Возможно, стоит проверить, правильно ли вы это делаете, если чувствуете, что вам нужно подумать. Я могу ответить на ваш вопрос только потому, что не знаю, чего вы пытаетесь достичь. Тем не менее, снижение производительности может быть приемлемым в зависимости от вашей ситуации. Не оптимизируйте преждевременно. - person Dave Williams; 21.08.2016
comment
Я нашел способ сделать то, что хотел, я установил настраиваемый атрибут со строкой для функций, затем я использую отражение, чтобы получить все функции с этим атрибутом и добавить в строку словаря, делегировать›, чтобы не было такого большого влияние на производительность, так как я использую отражение только один раз. - person Pau C; 22.08.2016

Если ваши функции содержат одинаковые signature, вы можете сделать что-то вроде этого

enum FunctionType
{
 Addition = 0,
 Substraction = 1,
 Mutiplication = 2,
 Division = 3
}
void ExecuteFunction(FunctionType Function)
{
  //variable will contain function to execute
  public Func<int, int, int> functionToExecute= null;

  switch(Function)
  {
    case FunctionType.Addition: functionToExecute=Addition;
    break;
    case FunctionType.Substraction: functionToExecute=Subsctration;
    break;      
    ...
    default: ...  
  }

  //Checking if not reached default case
  if(functionToExecute!=null)
  {
   var result= functionToExecute(para1,para2);
   ...............
  }


}
person Moumit    schedule 19.08.2016
comment
Я пытаюсь избежать переключения, это проблема в первую очередь. Если бы у меня было много функций, код был бы слишком длинным. - person Pau C; 19.08.2016