2016-12-15 7 views
1

Я работаю с C# и пытаюсь создать способ разобрать файл, в котором каждая строка является новой командой для программы, которую я имею ,C# Лучший способ создания объектов подкласса во время выполнения другой, тогда есть переключатель для каждой возможной опции

Мой файл выглядит примерно так:

  • двигаться 10 20
  • остановка 1000
  • ход 20 24

Сейчас у меня есть только два возможных инструкции, Переместить и Стоп, и у них разные параметры.

Я пытаюсь в будущем доказать свой код и сделать его как можно более общим, потому что в конечном итоге я добавлю больше команд в будущем.

Итак, я решил иметь команду абстрактного класса, которая реализует метод Update(), а затем команды наследуют от Command и указывают, что делает Update.

Эти команды заканчиваются списком, и я вызываю обновление для каждого элемента в списке.

(Это, вероятно, не лучшая реализация и не стесняйтесь предлагать лучшие из них)

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

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

Есть ли лучший способ для создания объектов данного подкласса во время выполнения другого, а затем через коммутатор для каждой возможной опции?

Заранее благодарен!

+0

Вы можете попробовать использовать Reflection, см. Этот ответ: http://stackoverflow.com/a/15452879/1220550 –

+0

@PeterB Это было быстро! Большое спасибо! Я всегда ухаживал за тем, чтобы отражение было плохим и медленным, и что нужно использовать дженерики, разве это возможно с дженериками? Для меня это была некоторая неопределенная территория. –

+0

Будьте предупреждены .... 'Activator.CreateInstance()' несколько величин ** медленнее ** чем сказать 'new Move()' – MickyD

ответ

2

В вашем простом сценарии, где синтаксис всегда будет [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>), когда они соответствуют, а затем создают агрегированный отчет об ошибках синтаксического анализа.

+0

Точно. Хорошо сказано – MickyD

+0

Это решение просто потрясающе! Я попробую это прямо сейчас. Большое спасибо. Идея здесь заключалась бы в создании моего списка команд с помощью функции 'ParseCommand'. Означает ли это, что моя реализация, когда я помещаю все команды в список и итерации, допустима ?. Еще раз спасибо вам большое. –

+0

@AlexandreLaborde Добро пожаловать! Да, вы создадите свой список команд, используя 'ParseCommand', а затем просто перейдете к нему, выполняя команды по порядку. Вероятно, вы захотите использовать 'Queue ' в этом случае, чтобы явно объявить, что порядок выполнения важен. – InBetween