Home > front end >  c# Simple Command System
c# Simple Command System

Time:01-19

I'm a c# begginer and I tried making a simple command system, all it suppose to do is to receive a string as input and based on that, give me a command based output, here is what I wrote:

using System;
using System.Collections;
using System.Collections.Generic;

namespace CommandShell
{
    class Program
    {
        public struct Command
        {
            public string commandInput;
            public string commandResult;

            public Command(string command, string commandResult)
            {
                this.commandInput = command;
                this.commandResult = commandResult;
            }
        }


        static void Main(string[] args)
        {
            List<Command> commands = new List<Command>();
            AddCommand("Open Door", "Door Opened", commands);
            AddCommand("Close Door", "Door Closed", commands);
            AddCommand("Shut Door", "Door Shuted", commands);

            void AddCommand(string commandInput, string commandResult, List<Command> commandsList)
            {
                Command newCommand = new Command(commandInput, commandResult);
                commandsList.Add(newCommand);
            }

            for (int i = 0; i < commands.Count; i  )
            {
                Command command = commands[i];

                if (command.commandInput.ToLower() == Console.ReadLine().ToLower())
                    Console.WriteLine(command.commandResult);
            }
        }

    }
}

The problem is that I have to write the commands in the order in which I added them because the "Console.ReadLine()" method is pausing the thread every time I try to check if the command is written, I want to be able to check for all the commends in the same time, any ideas?

CodePudding user response:

using System;
using System.Collections.Generic;

namespace CommandShell
{
    class Program
    {
        static Dictionary<string, Command> commands = new Dictionary<string, Command>(StringComparer.OrdinalIgnoreCase);

        public struct Command
        {
            public string commandInput { get; } // getter without setter, to make sure these are read-only once each instance is constructed
            public string commandResult { get; }

            public Command(string _commandInput, string _commandResult)
            {
                this.commandInput = _commandInput;
                this.commandResult = _commandResult;
            }
        }

        static void Main(string[] args)
        {
            AddCommand("Open Door", "Door Opened");
            AddCommand("Close Door", "Door Closed");
            AddCommand("Shut Door", "Door Shuted");

            void AddCommand(string commandInput, string commandResult)
            {
                commands.TryAdd(commandInput, new Command(commandInput, commandResult));
                // or you may insert new command into dictionary in the constructor
                // depends on what kinda flow do you like
            }

            while (true) // any condition to check if user is still using
            {
                string input = Console.ReadLine();

                if (commands.ContainsKey(input))
                    Console.WriteLine(commands[input].commandResult);
                else
                    Console.WriteLine($"Command {input} not found.");
            }
        }

    }
}

A simple sample, using Dictionary. I suggest you learn about it as it's really useful. Because of the mechanisms of Dictionary type, finding the input is usually faster than looking through every available command in Lists or Arrays. It's also more fit for your purpose, since we usually don't need orders for commands. Dictionary doesn't promise data order - which might be an issue in other cases than storing commands.

I also changed your struct a bit by adding Getter, { get; } means a property is read only, while { get; set; } allows both read and write to that property, just like a public property defined like in your original post. I figured read-only for these is better since a command doesn't usually need to have its name and description changed. Proper getter and setter can help you easier keeping track on your program, avoid accidentally changing data that's supposed to stay unchanged.

Notice I constructed the dictionary with StringComparer.OrdinalIgnoreCase, so you don't need to ToLower() every time you look into the dictionary. Just recently found out that we can construct with these options on this site, really useful for keeping codes clean. Once constructed with said option, accesses to that dictionary ignore case difference.

You can also look into Delegates, if you wish to trigger functions with commands.

I am recently writing similar program too, here are some problems I currently have in mind/dealt with recently: Is there a better, cleaner way, maybe more flexibility, to define commands? What if I want to have input parameters for some commands too, like Close Door /brutal, Open Door 2 or Open Box? Is there any better data structure for this job?

These can be some follow-ups for you too. But for now, I suggest that you can just look into what's in hand and have fun with designing from zero. Keep on thinking about what you want to make, what functionality you want to add, and go do that. One step at a time. Have fun learning CSharp!

CodePudding user response:

Instead of this

for (int i = 0; i < commands.Count; i  )
        {
            Command command = commands[i];

            if (command.commandInput.ToLower() == Console.ReadLine().ToLower())
                Console.WriteLine(command.commandResult);
        }

You can use like that i believe

var input = Console.ReadLine().ToLower();

if (commands.Exists(c => c.commandInput.ToLower() == input))
      Console.WriteLine(commands.Find(c => c.commandInput.ToLower() == input).commandResult);
else
      Console.WriteLine("Not exist!");

CodePudding user response:

It might be an opportune moment to introduce a bit of LINQ to your life:

    static void Main(string[] args)
    {
        List<Command> commands = new List<Command>();
        AddCommand("open door", "Door Opened", commands);
        AddCommand("close door", "Door Closed", commands);
        AddCommand("shut door", "Door Shuted", commands);
...
        var input = Console.ReadLine().ToLower();
        var cmd = commands.FirstOrDefault(command => command.commandInput == input );

        if (cmd == default)
            Console.WriteLine("Not a known command");
        else
            Console.WriteLine(command.commandResult);
        }
    }

FirstOrDefault internally will run a loop over your commands list. For every item it will call the bit of code in the parentheses command => command.commandInput == input which will returns boolean. This nugget of code is typically referred to as a lambda - it's like a method, but without a name, a stated return type, types for the parameters or a return keyword. It's the most compact form of:

bool IsWhatWeAreLookingFor(Command command){
  return command.commandInput == input;
}

As soon as it finds a true, FirstOrDefault will stop and return that command into the cmd variable

If you get an error about FirstOrDefault not existing, make sure you have using System.Linq; at the top of the file

  •  Tags:  
  • Related