I've made a pretty standard console in which you type a command and it does something. However, the issue that comes to my mind is scaling: if I want to add hundreds of commands, I have to manually add a new Command
instance for each one individually, which is... less than ideal.
The full code is stored in this GitHub repository.
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ConsolePlus.Commands;
namespace ConsolePlus
{
public class Program
{
/// <summary>
/// The version of the program.
/// </summary>
public const string Version = "1.0.0.0";
public static CommandRegistry<Command> Registry = new CommandRegistry<Command>();
/// <summary>
/// The application's entry point.
/// </summary>
/// <param name="args">The command-line arguments passed to the program.</param>
public static void Main(string[] args)
{
CommandHandler.RegisterCommands();
Console.WriteLine("ConsolePlus v." + Version);
while (true)
{
Console.Write(">>> ");
string line = Console.ReadLine();
List<string> parts = line.Split(' ').ToList<string>();
string commandName = parts[0];
parts.RemoveAt(0);
string[] commandArgs = parts.ToArray<string>();
try
{
string result = Registry.Execute(commandName, commandArgs);
if (result != null)
{
Console.WriteLine("[{0}] {1}", commandName, result);
}
}
catch (CommandNotFoundException)
{
Console.WriteLine("[ConsolePlus] No such command.");
}
}
}
}
}
CommandRegistry.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsolePlus
{
public class CommandRegistry<T>
where T : ICommand
{
private Dictionary<string, T> register;
public CommandRegistry()
{
register = new Dictionary<string, T>();
}
public CommandRegistry(params T[] commands)
: this()
{
foreach (T command in commands)
{
register.Add(command.Name, command);
}
}
public T GetCommand(string name)
{
if (register.ContainsKey(name))
{
return register[name];
}
else
{
throw new CommandNotFoundException(name);
}
}
public string Execute(string name, string[] args)
{
if (register.ContainsKey(name))
{
return register[name].Execute(args);
}
else
{
throw new CommandNotFoundException(name);
}
}
public void RegisterCommand(T command)
{
register.Add(command.Name, command);
}
}
}
ICommand.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsolePlus
{
public interface ICommand
{
string Name { get; set; }
string HelpText { get; set; }
bool IsPrivileged { get; set; }
string Execute(string[] args);
}
}
Command.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsolePlus
{
public class Command : ICommand
{
public string Name { get; set; }
public string HelpText { get; set; }
public bool IsPrivileged { get; set; }
private Func<string[], string> method;
public Command(string name, bool privileged, string help, Func<string[], string> commandMethod)
{
Name = name;
IsPrivileged = privileged;
HelpText = help;
method = commandMethod;
}
public string Execute(string[] args)
{
return method.Invoke(args);
}
}
}
CommandHandler.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsolePlus.Commands
{
public class CommandHandler
{
public static List<Command> AllCommands = new List<Command>
{
new Command("clear", false, ClearCommand.HelpText, ClearCommand.CommandMethod),
new Command("exit", false, ExitCommand.HelpText, ExitCommand.CommandMethod)
};
public static void RegisterCommands()
{
foreach (Command command in AllCommands)
{
Program.Registry.RegisterCommand(command);
}
}
}
}
And just for illustration (the other *Command
classes are very similar):
ClearCommand.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsolePlus.Commands
{
class ClearCommand
{
public static string HelpText = "Clears the console screen.";
public static string CommandMethod(string[] args)
{
Console.Clear();
return null;
}
}
}
At the moment, to add another command, I have to create another class and add another new Command
line to the CommandHandler.RegisterCommands
method - not ideal. Reviews of this specifically, and if there are any better ways to do this, would be very useful - though of course, any review is a good thing.