1

I'm building a console application to help expidite some stuff I do regularly. I have a menu with 4 options of procedures, which translate to different methods in my class.

Basicaly it's kind of like this:

What do you want to do?

1 This Thing

2 That Thing

3 Some Stuff

4 Cool Stuff

0 All The Stuff.

Input command string:_

Currently I'm checking for valid input with:

while (command.IndexOfAny("12340".ToCharArray()) == -1)
{
  //Display the menu and accept input
}

And then controlling flow with:

if (command.IndexOf("1") > 0 )
{
  thisThing();
}

if (command.IndexOf("2") > 0 )
{
  thatThing();
}

if (command.IndexOf("3") > 0 )
{
  someStuff();
}

if (command.IndexOf("4") > 0 )
{
  coolStuff();
}

if (command.IndexOf("0") > 0 )
{
  thisThing();
  thatThing();
  someStuff();
  coolStuff();
}

The goal is to provide input and run one or more processes as indicated:

1 : thisThing()

13 : thisThing() and someStuff();

42 : thatThing() and coolStuff();

0 : run all processes I have defined.

Is there a way to do something like this with better practices?

Community
  • 1
  • 1
Mr. Tim
  • 886
  • 8
  • 18
  • 1
    Well honestly, I'd create a gui in win forms or WPF and just map the button clicks to the processes. Also, you can't, with this method, run a command twice in the event you need to do thisThing() => someStuff() => thisThing(). – C Bauer Sep 11 '14 at 14:56
  • Yes, I thought of using a GUI with buttons as well, but I guess I decided on console for simplicity. I'm really new to this stuff, so GUI stuff intimidates me a little bit. Currently this is just a small part of something that could be broken into a module of a larger thing -- at which point something like this should probably be done. Right now, it's just an .exe that helps me set up a new client in a database, with several tasks that may or may not need to be done. Thanks for your time! – Mr. Tim Sep 11 '14 at 16:27

4 Answers4

2

I would create an Dictionary<char, DoSomething>

public delegate void DoSomething();

Dictionary<char, DoSomething> commands = new Dictionary<char, DoThing>();
commands.Add('0', new DoSomething(DoAll));
commands.Add('1', new DoSomething(ThisThing));
commands.Add('2', new DoSomething(ThatThing));
commands.Add('3', new DoSomething(SomeStuff));
commands.Add('4', new DoSomething(CoolStuff));

Then, after input validation

foreach(char c in command.ToCharArray()) 
{  
   // Better check if the input is valid
   if(commands.ContainsKey(c))
       commands[c].Invoke();
}

The Dictionary contains, as keys, the chars allowed and, as values, the delegate to a function with void return and no arguments. Now it is just a matter of looping on the input char by char and invoke the associated method.

Keep present that this approach, with so few choiches, is no better that a simple if/else if/else or switch/case. Also in this way, your user could type 24 or 42 inverting the order of execution of the methods and this could not be allowed in your real context.

Steve
  • 213,761
  • 22
  • 232
  • 286
  • This seems like a pretty cool way to go about this. I guess it ends up being a little similar to how I was originally parsing the string (an if (string.contains("")) statement for each function) but I like that there is a solid, obvious place where the "commands" menu is described. I need to learn a little bit more about delegates. Thanks for pointing me in a direction! – Mr. Tim Sep 11 '14 at 16:34
1

I'd create a Dictionary of delegates. Dictionary key would be the input value, and the delegate would execute the code.

The syntax is covered here C# Store functions in a Dictionary.

This way you can step the input string in a loop and do a dictionary lookup; if the value exists execute the delegate, otherwise skip.

Its not specifically better than a If else if endif set, although I'd find it more attractive and extensible.

Community
  • 1
  • 1
PhillipH
  • 6,182
  • 1
  • 15
  • 25
  • Thanks for your answer PhillipH! With Steve's example, and your link, I've got a better handle on some alternate ways to handle this. Thanks! – Mr. Tim Sep 11 '14 at 16:36
1

I have found a very nice way to do it based on regexes in a Ayende Rahien project where he parses the content of the Freedb.org data files

Basically the parser sets up a list of Tuple<Regex, Action<contextual class>> with the contextual class changing depending on the place where the parser is at the time. Parsing then becomes trying to match each element (in this case line) with a regex and executing the corresponding action if it matches.

In your case you could be able to link your commands to multiple inputs, eg

public class Parser
{
    readonly List<Tuple<Regex, Action>> actions = new List<Tuple<Regex, Action>>();
    public Parser()
    {
        Add(@"^0$", () => { DoSomething(); });
        Add(@"^DoSomething$", () => { DoSomething(); });

        Add(@"^1$", () => { DoSomethingElse() });
        Add(@"^DoSomethingElse$", () => { DoSomethingElse() });

        // etc
    }
    private void Add(string regex, Action action)
    {
        var key = new Regex(regex, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace | RegexOptions.Multiline);
        actions.Add(Tuple.Create(key, action));
    }

    public void Parse(string text)
    {
        foreach (var action in actions)
        {
            var collection = action.Item1.Matches(text);
            try
            {
                action.Item2();
            }
            catch (Exception e)
            {
                // log?
                throw;
            }
        }
    }
}

which lets you run the methods you want with differents inputs.

samy
  • 14,832
  • 2
  • 54
  • 82
  • I don't know why this one isn't higher rated. Is it because it's over-engineered for the specific problem? I was extremely lucky to come across it, since it's exactly what I need! – Jesse Smith Jul 26 '15 at 13:52
  • Thanks @JesseSmith . It may be overenginereed in this case, but it is a really simple and reusable way of parsing data files. Glad it could help, and don't hesitate to spread its use – samy Jul 27 '15 at 06:32
0

I'd create an Interface which all commands derive from and have a switch statement determine which action to execute. Something along the lines of...

public abstract class ISystem
{
    public abstract void DoSomething();
}

public class ThisThing : ISystem
{
    public override void DoSomething()
    {
        Console.WriteLine("Do This Thing");
    }
}

public class ThatThing : ISystem
{
    public override void DoSomething()
    {
        Console.WriteLine("Do That Thing");
    }
}

static void Main(string[] args)
{
    var input = "-1";
    while(/*input is invalid*/)
    {
        // get input from user
    }

    var thisThing = new ThisThing();
    var thatThing = new ThatThing();

    switch(input)
    {
        case "1":
           {
               thisThing.DoSomething();
               break;
           }
        case "2":
           {
               thatThing.DoSomething();
               break;
           }
        case "0":
           {
               var commands = new List<ISystem>();
               commands.Add(thisThing);
               commands.Add(thatThing);

               // Execute all commands
               commands.ForEach(system => system.DoSomething());
               break;
           }
    }
}

The advantage here is that every time a new command is added then it just needs to implement the DoSomething() method, after that it's just the trivial task of creating an instance of the class var someStuff = new SomeStuff(); and then added to the list of commands in the case "0":

Plus IMO the intention is clearer to read than managing a Dictionary etc.

Carl Burnett
  • 171
  • 1
  • 8
  • Hi Carl, thanks for your time. I should have specified that all of the methods are very procedural in nature, and are currently set static. So, in your example, you would take them and turn the various procedures into objects, with a method of .DoSomething()? Sorry, I'm really new to programming architecture and c#, so I'm not totally following. – Mr. Tim Sep 11 '14 at 16:31
  • Yes that is exactly it. In my example you create an instance of each of the commands you have/need/want and depending on what the user has input you call __that__ commands `.DoSomething()` method. In the case that you wish to call *all* of the commands, because they all implement the same interface then you don't need to know or care what the object types are (ThisThing, ThatThing, SomeStuff) and just have a list of the Interface type (in my example that is `ISystem`) That's why you can just do the one-liner `commands.ForEach(system => system.DoSomething());` I hope that answers your question? – Carl Burnett Sep 11 '14 at 16:52