I am making a text adventure in C#, and someone suggested that I use a dispatch table instead of a switch statement.
Here's the switch statement code:
        #region Public Methods
        public static void Do(string aString)
        {
                if(aString == "")
                        return;
                string verb = "";
                string noun = "";
                if (aString.IndexOf(" ") > 0)
                {
                        string[] temp = aString.Split(new char[] {' '}, 2);
                        verb = temp[0].ToLower();
                        noun = temp[1].ToLower();
                }
                else
                {
                        verb = aString.ToLower();
                }
                switch(Program.GameState)
                {
                        case Program.GameStates.Playing:
                                if (IsValidInput(Commands, verb, true))
                                {
                                        switch(verb) //this is the switch statement
                                        {
                                                case "help":
                                                case "?":
                                                        WriteCommands();
                                                        break;
                                                case "exit":
                                                case "quit":
                                                        Program.GameState = Program.GameStates.Quit;
                                                        break;
                                                case "move":
                                                case "go":
                                                        MoveTo(noun);
                                                        break;
                                                case "examine":
                                                        Examine(noun);
                                                        break;
                                                case "take":
                                                case "pickup":
                                                        Pickup(noun);
                                                        break;
                                                case "drop":
                                                case "place":
                                                        Place(noun);
                                                        break;
                                                case "use":
                                                        Use(noun);
                                                        break;
                                                case "items":
                                                case "inventory":
                                                case "inv":
                                                        DisplayInventory();
                                                        break;
                                                case "attack":
                                                        //attack command
                                                        break;
                                        }
                                }
                                break;
                        case Program.GameStates.Battle:
                                if(IsValidInput(BattleCommands, verb, true))
                                {
                                        switch(verb) //this is the other switch statement
                                        {
                                                case "attack":
                                                        //attack command
                                                        break;
                                                case "flee":
                                                case "escape":
                                                        //flee command
                                                        break;
                                                case "use":
                                                        //use command
                                                        break;
                                                case "items":
                                                case "inventory":
                                                case "inv":
                                                        //items command
                                                        break;
                                        }
                                }
                                break;
                }
        }
        #endregion
How do I refactor this to use a dispatch table?
Simplest way would be to use a dictionary of delegates.
For example:
Dictionary<string, Action> dispatch = new Dictionary<string, Action>();
dispatch["help"] = new Action(() => Console.WriteLine("Hello"));
dispatch["dosomething"] = new Action(() =>
{
    // Do something else
    Console.WriteLine("Do Something");
});
// Call the 'help' command
dispatch["help"]();
For multiple different parameters it might be simplest to use a base Delegate and use dynamic Invoke.
Dictionary<string, Delegate> dispatch = new Dictionary<string, Delegate>();
dispatch["help"] = new Action(() => Console.WriteLine("Hello"));
dispatch["dosomething"] = new Action<string>(s => Console.WriteLine(s));
dispatch["help"].DynamicInvoke();
dispatch["dosomething"].DynamicInvoke("World");
And if using .NET 4 you can also use dynamic types to resolve at run time to reduce the clutter slightly of dynamic invoke.
Dictionary<string, dynamic> dispatch = new Dictionary<string, dynamic>();
dispatch["help"] = new Action(() => Console.WriteLine("Hello"));
dispatch["dosomething"] = new Action<string>(s => Console.WriteLine(s));
dispatch["help"]();
dispatch["dosomething"]("World");
                        Maybe he was refering to 'Double Dispatch', or the Visitor Pattern.
You could split up your code to improve a more 'dispatcher' style as follows:
public interface IGameState{
  void Help();
  void Question();
  void Attack();
}
public interface ICommand{
  bool IsValidFor(PlayingState state);
  bool IsValidFor(BattleState state);
  void Execute(IGameState state);
}
public class PlayingState : IGameState {
   public void Help(){ // Do Nothing }
   public void Question() { WriteCommands(); }
   private void WriteCommands(){ }
}
public class Battle : IGameState{
   public void Help(){ // Do Nothing }
   public void Question() { WriteCommands(); }
   public void Attack() { Roll(7); }
   private void Roll(int numDice){ }
}
public class CommandBuilder{
  public ICommand Parse(string verb){
    switch(verb){
       case "help":
         return new HelpCommand();
       case "?":
         return new QuestionCommand();
       case "attack":
         return new AttackCommand();
       default:
         return new UnknownCommand();
    }
  }
}
public class QuestionCommand(){
  bool IsValidFor(PlayingState  state){
     return true;
  }
  bool IsValidFor(BattleState state){
     return false;
  }
  void Execute(IGameState state){
     state.Question();
  }
}
public static void Do(string aString){
  var command = CommandBuilder.Parse(aString);
  if(command.IsValidFor(Program.GameStates))
     command.Execute(Program.Gamestates);
}
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With