3

I am making a debugging console for my projects, taking input from a TextBox, etc.

However I wish to know if there are any alternatives to simply a huge switch statement:

switch(command.ToLower) {
case "example":
    // do things...
    break;
case "example2":
    // do things...
    break;
}

As I feel there is a more elegant solution avaliable but my skills cannot access it.

EDIT: Due to the amazing contribution of @OwlSolo I now have my code working, I have posted below my modified version of the code submitted that is functioning for me. Thanks @OwlSolo you are an legend typing!

class parseCommand
{
    public static commandBase commandInstance { get; set; }

    public static void parse(string command)
    {
        string[] tokens = command.Split(' '); // tokens[0] is the original command
        object[] parameters = tokens.Skip(1).ToArray();

        List<Type> cmdTypes = System.Reflection.Assembly
            .GetExecutingAssembly()
            .GetTypes()
            .Where(t => typeof(commandBase).IsAssignableFrom(t))
            .ToList();

        foreach(Type derivedType in cmdTypes)
        {
            if (derivedType.Name.ToLower() == tokens[0].ToLower())
            {
                commandInstance = (commandBase)Activator.CreateInstance(derivedType);
                commandInstance.parameters = parameters;
                commandInstance.Execute();
                break;
            }
        }
    }
}

4 Answers 4

3

Parsing a language of some kind is basically a whole discipline in itself, so this question is rather broad. Language lexers and parsers generally create tree structures of commands which are separated in reserved keywords and parameters. The reserved keywords contain for instance commands. (such as switch, if, goto, etc. in C-like languages) The thing is, that these commands are ideally chosen in a way so that they're mutually independent. That means that the keywords in itself provoke a very different handling. The fine tuning is done via the parameters.

If this applies to your commands, you don't have much of a choice as to provide independent methods of handling each command. For instance the JavaCC (JavaCompiler-Compiler) generates a code base with rather large switch cases that generate an instruction tree. It is then up to the user to evaluate the provided instruction tree which is usually done via individual objects that handle the keywords - so there could be a class IfStatement which holds a number of child instructions and handles its execution.

Whatever you need here specifically, the real work is going to be, how you handle the execution rather than how you differentiate which command invokes which behavior.

A structure you might want could look something like this:

abstract class BinaryCommand
{
    MyBaseCommand child1;
    MyBaseCommand child2;

    abstract object Execute();
}

class ExampleCommand1 : BinaryCommand
{
    override object Execute()
    {
         //DoStuff1...
    }
}

class ExampleCommand2 : BinaryCommand
{
    override object Execute()
    {
         //Do Somethign else
    }
}

As for differentiating between your keywords, there is a number of ways:

  • A large switch statement.

  • Holding a Dictionary<string, Type> from which you look up the Type that handles a command. So for instance: "Example1 abcde 12345" would lookup "Example1", create an instance of the type in the dictionary and fil it with the parameters "abcde" and "12345".

  • A rather bold way would be to reflect through your code for a class that can handle the command.
    You would have an interface like IBaseCommand from which all you command classes derive.

// Get all the types that derive from your base type
List<Type> commandTypes = Assembly
    .GetExecutingAssembly()
    .GetTypes()
    .Where(t => typeof(IBaseCommand).IsAssignableFrom(t));

foreach (Type derivedType in commandTypes)
{
    // Distinguishing by class name is probably not the best solution here, but just to give you the general idea
    if (derivedType.Name == command.ToLower) 
    {
        // Create an instance of the command type
        IBaseCommand myCommandInstance = Activator.CreateInstance(derivedType);
        //Call the execute method, that knows what to do
        myCommandInstance.Execute();
    }
}

EDIT: With the information provided in the comments, you could do something like this

Interface ICommandBase
{
    object[] parameters {get; set;}
    void Execute();    
}

abstract class InternalCommand : ICommandBase
{
    //Some methods that are common to all your intzernal commands
}

class SetColorCommand : InternalCommand //Or it might derive from ICommandBase directly if you dont need to categorize it
{
     object[] parameters {get; set;}
     void Execute()
     {
         switch (parameters[0])
         {
              case "background":
                  //Set the background color to parameters[1]
              break;
              case "foreground":
                    //...
              break;
         }
     }
}

class SqlCommand : ICommandBase
// Or it might derive from ICommandBase directly if you don't need to categorize it
{
     object[] parameters {get; set;}
     void Execute()
     { 
          //Parameters[0] would be the sql string...
     } 
}

Then parse the whole thing via:

// Assuming you're getting one command per line and one line is fed to this function
public void ParseCommands(string command)
{
    string[] tokens = command.Split(" ");
    // tokens[0] is the command name
    object[] parameters = (object[])tokens.Skip(1);//Take everything but the first element (you need to include LINQ for this)

    // Get all the types that derive from your base type
    List<Type> commandTypes = Assembly
        .GetExecutingAssembly()
        .GetTypes()
        .Where(t => typeof(IBaseCommand).IsAssignableFrom(t));

    foreach (Type derivedType in commandTypes)
    {
        if (derivedType.Name.ToLower == tokens[0].ToLower) 
        /* Here the class name needs to match the commandname; this yould also be a
           static property "Name" that is extracted via reflection from the classes for 
           instance, or you put all your commands in a Dictionary<String, Type> and lookup 
           tokens[0] in the Dictionary */
        {
            // Create an instance of the command type
            IBaseCommand myCommandInstance = Activator.CreateInstance(derivedType);
            myCommandInstance.parameters = parameters;
            myCommandInstance.Execute(); // Call the execute method, that knows what to do
                 break;
        }
    }   
}

You goal is to have as few commands as possible and do as much as you can via the parameters.

Sign up to request clarification or add additional context in comments.

6 Comments

Could you please expand on how I would setup the above structure and it's implementation into a project. Sorry if I'm asking for too much and feel free to do nothing, you've already made an amazing answer!
It would highly reduce the work if you give an example what your commands actually are. I suppose you will need parameters for them. If so, how many, what kind, etc.?
Generally they would consist of remote commands that would be serialized (I can already do that) and sent across a TCP socket to my projects. However if it is possible I would like some internal commands such as colour settings etc.
Can you provide a specific example? what would the serialized string you extract the commands from look like? "Command param1 param2 param3 EndOfLine Command param1 param2...."? So for instance "SetColor MyControl 255 127 127"?
Example: colour_set background #F5F5DC (internal command) and sql_cmd "INSERT INTO properties ..."
|
1

not really... the only thing i would do is split up the different types of commands into different methods to make it more streamlined/elegant, and use generic collections to store the commands that apply to each type.

example:

List<string> MoveCommands = new List<string>(){"Move", "Copy", "Merge"};
List<string> Actions = new List<string>() {"Add", "Edit", "Delete"};

//.......

if(MoveCommands.contains(inputtext))
    ExecuteMoveCommand();
else if (Actions.contains(inputtext))
    ExecuteActionCommand();

stuff like that... the route you are taking only leaves elegance and code neatness open to play.

Comments

0

You can do something like:

var lookup = new Dictionary<string, YourParamType> ()  
{
  { "example", a  },
  { "example2", b },
   ....
};

YourParamType paraType;
if (lookup.TryGetValue(command.ToLower(), out para))   // TryGetValue, on popular      request
{       
   //your code
}

Comments

0

One year ago I've solved the same problem. So I'll explain how it works using my code as an example, so you will know how to design command line parsers and how to solve your problem.

As OwlSolo already said you need a base class or an interface which will be able to accept arguments and execute some logic.

In case of the Cmd.Net it's the Command class:

public abstract class Command
{
    protected Command(string name);
    public string Name { get; }
    public abstract void Execute(ArgumentEnumerator args);
}

The class has two members:

  1. The property Name which provides a name for the command
  2. The method Execute which accepts arguments and do the business logic.

The ArgumentEnumerator splits the provided string like the string.Split method mentioned above in OwlSolo's code, but in a more complex way. It produces key-value pairs of the argument's name and its value.

As an example, you have a string which looks like this:

"/namedArg:value1 value2"

will be parsed into two pairs. The first pair is a named argument with the name "namedArg" and the value "value1". The second is an unnamed argument (name equals to string.Empty) with the value "value2".

The purpose of named arguments is to allow to rearrange them and make some of them optional. This should improve usability.

Now we want to have a collection of commands from which you can faster take one of them by the name. And the Dictionary<string, Command> is the best choice, but let's see further and make a command which will transfer control to a child command. So we will be able to build command categories/hierarchies like in netsh.

public sealed class CommandContext : Command
{
    public CommandContext(string name);
    public CommandCollection Commands { get; }
    public override void Execute(ArgumentEnumerator args);
}

public sealed class CommandCollection : KeyedCollection<string, Command>
{
    public CommandCollection() 
        : base(StringComparer.OrdinalIgnoreCase) 
    { 
    } 

    protected override string GetKeyForItem(Command item)
    { 
        return item.Name;
    }

    public bool TryGetCommand(string name, out Command command)
    { 
        return Dictionary.TryGetValue(name, out command);
    }
}

The overridden Execute method will take the first argument if it's unnamed and search for the command using the TryGetCommand method. When it finds the command, it executes it with the all arguments except the first one. If there is no command found or the first argument has a name, then we should show an error.

NOTE Since we use StringComparer.OrdinalIgnoreCase we should not worry about character cases in the passed name.

Now it's time to think about automated argument parsing and converting. To achieve this we can use reflection and TypeConverters.

public sealed class DelegateCommand : Command
{
    public DelegateCommand(Delegate method);
    public Delegate Method { get; }
    public override void Execute(ArgumentEnumerator args);
}

In the DelegateCommand's constructor you should gather information about method's parameters (names, default values, type converters, etc.) and later use that in the Execute method to cast and provide arguments to the method.

I omitted implementation details because it's complex, but you can read about that in DelegateCommand.cs and in Argument.cs.

Finally you will be able to execute methods without any parsing in it.

CommandContext root = new CommandContext(
    "root",
    new Command("multiply", new Action<int, int>(Multiplication)),
    new CommandContext(
        "somecontext",
        // ...
        )
    );
ArgumentEnumerator args = new ("add /x:6 /y:7");

root.Execute(args);

public static void Multiplication([Argument("x")] int x, [Argument("y")] int y)
{
    // No parsing, only logic
    int result = x * y;
}

2 Comments

Jeez that's complicated. I think I'll pass on the CMD.NET implementation, but thanks for the input!
Therefore it's a whole discipline as @OwlSolo mentioned.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.