Я пишу утилиту командной строки в C # для .NET Core. Я хочу позволить пользователю указать «действие» для запуска на основе параметра командной строки. Я имитирую параметры командной строки в стиле PowerShell, поэтому один из моих вариантов - /Action
. Так, например, пользователь может вызвать приложение с /Action:Update
или /Action:Reset
.
В C # у меня есть метод для каждого действия, которое следует за определенной сигнатурой метода. Итак, для метода Update
, описанного выше, у меня есть такой метод: public static int Update(Dictionary<string,string> cmdLineArgs, SomeObject obj)
. Каждый метод, связанный с действительным параметром в /Action
, имеет одинаковую сигнатуру (одинаковые типы и имена переменных).
Сейчас у меня просто есть блок switch
для вызова действий, но это кажется невероятно неэффективным:
int returnValue;
switch (parsedArgs["action"]) {
case "update":
returnValue = Update(parsedArgs, o);
break;
case "reset":
returnValue = Reset(parsedArgs, o);
break;
...
default:
returnValue=255;
Console.WriteLine($"No such action {parsedArgs["action"]}.");
break;
}
Я использовал атрибуты в контексте веб-API, и они кажутся естественной отправной точкой, чтобы сделать это более общим. В идеале это привело бы к ситуации, когда добавление нового действия так же просто, как написание его метода и добавление правильного атрибута с именем, по которому пользователь может вызвать его с помощью переключателя /Action
. Моя идея состоит в том, чтобы создать пользовательский атрибут (скажем, AppActionName
), а затем поместить этот атрибут в любой метод, который можно вызвать как действие из командной строки:
[AppActionName("update")]
public static int Update(Dictionary<string,string> cmdLineArgs, SomeObject obj)
...
[AppActionName("reset")]
public static int Reset(Dictionary<string,string> cmdLineArgs, SomeObject obj)
...
Альтернативой, о которой я подумал, которая могла бы использовать преимущества безопасности типов, было бы использование интерфейса, определяющего метод действия:
public interface IAppAction
{
int Run(Dictionary<string,string> cmdLineArgs, SomeObject obj);
}
[AppActionName("update")]
public class UpdateAction : IAppAction
{
public int Run(Dictionary<string,string> cmdLineArgs, SomeObject obj)
...
[AppActionName("reset")]
public class ResetAction : IAppAction
{
public int Run(Dictionary<string,string> cmdLineArgs, SomeObject obj)
...
В любом случае, в чем я не уверен, так это в том, как на самом деле искать, создавать и запускать метод.
В первом варианте (помещение AppActionName непосредственно в метод) я вижу две проблемы: 1) необходимость выяснить, как искать во всех методах в сборке те, у которых есть заданный атрибут, фильтрация, а затем как на самом деле вызовите метод и 2) если я не знаю, как это сделать, я не думаю, что с помощью этого метода смогу принудительно установить правильную сигнатуру метода.
int returnValue;
// in other languages you can get a variable and then call it, but this isn't other languages
// but you might do something like: myMethod = findMethodWithAttribute("update"); returnValue=myMethod(parsedArgs, o);
Второй вариант (интерфейс на интерфейсе, реализующем класс) кажется более безопасным с точки зрения типов и должен быть проще в реализации (объявить переменную интерфейса, а затем назначить ее экземпляру правильного класса), но я все еще не уверен, как на самом деле найти атрибут с правильным именем.
int returnValue;
// how would you do this correctly?
IAppAction appActionClass = new FindTheClassWithTheAttributeWithParameter("update")();
returnValue = appActionClass.Run(parsedArgs, o);
Так что я думаю, что суть моего вопроса такова: «Как мне найти, какой метод / класс имеет атрибут, который я определил с указанным параметром, и как мне на самом деле создать / вызвать результат?»