В вашем простом сценарии, где синтаксис всегда будет [Command] [Space separated numeric argument list]
вы могли бы рассмотреть следующий подход:
Реализовать Dictionary<string, Func<IEnumerable<int>, Command>>
где ключ будет зарезервированным словом командного и значение делегата, который будет принимать разобранные строковые аргументы (Я взял на себя смелость предположить, что все они будут целыми) и построить соответствующую команду. Эти делегаты могли бы указать на статические фабричные методы, реализованных в Command
аналогичен тому, как Linq.Expressions
построен:
public abstract class Command
{
private const int validMoveArgumentCount = 2;
private const int validStopArgumentCount = 1;
public static Command Move(IEnumerable<int> args)
{
if (args == null)
throw new ArgumentNullException(nameof(args));
var arguments = args.ToArray();
var argumentCount = arguments.Length;
if (argumentCount != validMoveArgumentCount)
throw new SyntaxErrorException("Invalid number of arguments in Move command. Expected {validMoveArgumentCount}, received {argumentCount}.");
return new MoveCommand(arguments[0], arguments[1]);
}
public static Command Stop(IEnumerable<int> args)
{
if (args == null)
throw new ArgumentNullException(nameof(args));
var arguments = args.ToArray();
var argumentCount = arguments.Length;
if (argumentCount != validStopArgumentCount)
throw new SyntaxErrorException("Invalid number of arguments in Stop command. Expected {validStopArgumentCount}, received {argumentCount}.");
return new StopCommand(arguments[0]);
}
public abstract void Update();
...
private class MoveCommand: Command { ... }
private class StopCommand: Command { ... }
}
Обратите внимание на рисунок, я действительно наслаждаюсь много; вложенные частные классы, которые происходят из содержащего абстрактный класс. Это аккуратный способ полностью скрывать выполнение конкретных команд и точно контролировать, кто может их создавать.
Вы должны были бы иметь метод инициализации, на котором вы строите словарь, то вдоль линий:
var commandDictionary = new Dictionary<string, Func<IEnumerable<int>, Command>>();
commandDictionary["move"] = args => Command.Move(args);
commandDictionary["stop"] = args => Command.Stop(args);
Это, по существу, где вы телеграфировать свой синтаксический анализ логики.
И теперь при анализе каждой строки вашего ввода команд, вы могли бы сделать что-то подобное (упрощаю Каус):
private Command ParseCommand(string commandLine)
{
Debug.Assert(!string.IsNullOrWhiteSpace(commandLine));
var words = commandLine.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
if (commandDictionary.ContainsKey(words[0]))
return commandDictionary[words[0]](words.Skip(1).ParseIntegerArguments());
throw new SyntaxErrorException($"Unrecognized command '{words[0]}'.");
}
private static IEnumerable<int> ParseIntegerArguments(IEnumerable<string> args)
{
Debug.Assert(args != null);
foreach (var arg in args)
{
int parsedArgument;
if (!int.TryParse(arg, out parsedArgument))
throw new SyntaxErrorException("Invalid argument '{arg}'");
yield return parsedArgument;
}
}
Все, что сказал, здесь вы не используете переключатель заявление, но вы создание словаря. В конце дня вам нужно как-то определить, какие команды действительны и как вы собираетесь их обрабатывать. Возможно, при таком подходе добавление новых команд немного чище, но это зависит от личного вкуса.
Также стоит упомянуть, что для простоты я бросаю исключения, когда сталкиваюсь с неправильным синтаксисом. Это было бы не так, как я бы это сделал, если бы я серьезно применял подобный парсер, потому что, как сказал бы Эрик Липперт, я бы бросил особенно vexing exceptions. Я бы, вероятно, просто проанализировал все это как можно лучше (восстановление ошибок довольно тривиально в данном конкретном случае), добавляя дескриптивные объекты ошибки к diagnosticsBag
(некоторые IList<ParsingError>
), когда они соответствуют, а затем создают агрегированный отчет об ошибках синтаксического анализа.
Вы можете попробовать использовать Reflection, см. Этот ответ: http://stackoverflow.com/a/15452879/1220550 –
@PeterB Это было быстро! Большое спасибо! Я всегда ухаживал за тем, чтобы отражение было плохим и медленным, и что нужно использовать дженерики, разве это возможно с дженериками? Для меня это была некоторая неопределенная территория. –
Будьте предупреждены .... 'Activator.CreateInstance()' несколько величин ** медленнее ** чем сказать 'new Move()' – MickyD