Map commands to methods - c#

I have a command line app where I map commands to methods, using a dictionary from the one-letter command to the name of the method (as a string). I can use this method both to invoke it and also to tell the user the list of available commands, like so:
private static readonly Dictionary<string, string> commands =
new Dictionary<string, string>
{
{"u", "DestroyUniverse"},
{"g", "DestroyGalaxy"},
{"p", "DestroyPlanet"},
{"t", "TimeTravel"}
};
public void DestroyUniverse(Stack<string> argStack)
{
// destroy the universe according to the options in argStack
// ...
}
public void DestroyGalaxy(Stack<string> argStack)
{
// destroy the galaxy according to the options in argStack
// ...
}
// ... other command methods
public void Run(Stack<string> argStack)
{
var cmd = argStack.Next();
string methodName;
// if no command given, or command is not found, tell
// user the list of available commands
if (cmd == null || !commands.TryGetValue(cmd, out methodName))
{
Console.WriteLine("Available Commands:{0}{1}",
Environment.NewLine,
string.Join(Environment.NewLine,
commands.OrderBy(kv => kv.Key)
.Select(kv => string.Format("{0} - {1}", kv.Key, kv.Value))));
Environment.ExitCode = 1;
return;
}
// command is valid, call the method
GetType().GetMethod(methodName).Invoke(this, new object[] {argStack});
}
This is working fine, except I don't like that I'm using strings as the value in the dictionary. There is thus no compiler support for making sure there is a method for every string. I'd rather use "methods" somehow, but still have access to the name of the method, for the part where I list the commands. Is there anything like that available?

Why doesn't this work?
class Program
{
static void Main(string[] args)
{
var p = new Program();
p.Run(new Stack<string>(args.Reverse()));
Console.ReadKey();
}
private readonly Dictionary<string, Action<Stack<string>>> commands;
public Program() {
commands =
new Dictionary<string, Action<Stack<string>>>
{
{"u", DestroyUniverse },
{"g", DestroyGalaxy },
{"p", DestroyPlanet },
{"t", TimeTravel }
};
}
public void DestroyUniverse(Stack<string> argStack)
{
// destroy the universe according to the options in argStack
// ...
}
public void DestroyGalaxy(Stack<string> argStack)
{
// destroy the galaxy according to the options in argStack
// ...
}
private string Next(Stack<string argStack)
{
// wish this was a method of Stack<T>
return argStack.Any() ? argStack.Pop() : null;
}
public void Run(Stack<string> argStack)
{
var cmd = Next(argStack);
Action<Stack<string>> action = null;
// if no command given, or command is not found, tell
// user the list of available commands
if (cmd == null || !commands.TryGetValue(cmd, out action))
{
Console.WriteLine("Available Commands:{0}{1}",
Environment.NewLine,
string.Join(Environment.NewLine,
commands.OrderBy(kv => kv.Key)
.Select(kv => string.Format("{0} - {1}",
kv.Key, kv.Value.Method.Name))));
Environment.ExitCode = 1;
return;
}
// command is valid, call the method
action(argStack);
}
}

You can use Reflection. Get a MethodInfo of all the methods you're intrested in and put them in the dictionary. Later then you can invoke one of them. If you need the name of the method as string, you can get it also from the MethodInfo.

Related

C# How to use lambda expression with dictionary's value which is a method

I'm creating a program which will execute a command after user input.
Some commands I want to implement are: creating, reading a file, getting current working directory etc.
I created a dictionary which will store user input and corresponding command:
public static Dictionary<string, Action<string[]>> Commands { get; set; } = new Dictionary<string, Action<string[]>>()
{
{"pwd", PrintWorkingDirectory },
{"create", CreateFile },
{"print", ReadFile },
};
Unfortunately I have issues with triggering the method:
public void Run()
{
Console.WriteLine("Welcome, type in command.");
string input = null;
do
{
Console.Write("> ");
input = Console.ReadLine();
Execute(input);
} while (input != "exit");
}
public int Execute(string input)
{
if(Commands.Keys.Contains(input))
{
var action = Commands.Values.FirstOrDefault(); //doesn't work, gives '{command} not found'
}
Console.WriteLine($"{input} not found");
return 1;
}
Also I noticed that this solution would not work with method which is not void, but returns something, as for example CreateFile.
public static string CreateFile(string path)
{
Console.WriteLine("Create a file");
string userInput = Console.ReadLine();
try
{
string[] file = userInput.Split(new char[] { ' ' }).Skip(1).ToArray();
string newPath = Path.GetFullPath(Path.Combine(file));
using (FileStream stream = new FileStream(newPath, FileMode.Create, FileAccess.ReadWrite))
{
stream.Close();
}
using (StreamWriter sw = new StreamWriter(newPath))
{
Console.WriteLine("Please type the content.Press Enter to save.");
sw.WriteLine(Console.ReadLine());
sw.Close();
Console.WriteLine("File {0} has been created", newPath);
}
}
catch (Exception)
{
throw;
}
return path;
}
public static void ReadFile(string[] args)
{
Console.WriteLine("Reading file");
string userInput = Console.ReadLine();
string[] file = userInput.Split(new char[] { ' ' }).Skip(1).ToArray();
string newPath = Path.GetFullPath(Path.Combine(file));
string[] lines = File.ReadAllLines(newPath);
foreach (string line in lines)
Console.WriteLine(line);
}
public static void PrintWorkingDirectory(string[] args)
{
var currentDirectory = Directory.GetCurrentDirectory();
Console.WriteLine(currentDirectory);
}
Could somebody advise me how to deal with these issues?
Is it that this dictionary I created does not make much sense at all?
First problem: You're always fetching the first element of the dictionary and are not using the index operator to retrieve the correct value. Therefore change:
if(Commands.Keys.Contains(input))
{
var action = Commands.Values.FirstOrDefault(); //doesn't work, gives '{command} not found'
}
to:
public int Execute(string input)
{
if (Commands.Keys.Contains(input))
{
var action = Commands[input]; //doesn't work, gives '{command} not found'
action?.Invoke(new string[] { });
}
else
{
Console.WriteLine($"{input} not found");
}
return 1;
}
Regarding to your second question about dictionary usage. I think it is ok to use a dictionary to map different commands based on a given key. The alternative would be switch or if constructs, which can be prevented in Object Oriented Programming.
Regarding to your question about string CreateFile(string path). Since C# is strongly typed language your dictionary can only contain objects of type Action<string[]>, so you can't use methods with another signature than that. One solution is to add another dictionary in the form of Dictionary<string,Func<string[], string>. As a result you'll get more and more dictionaries depending on your method signatures. From here on you should think to build to encapsulate your commands in an e.g. CommandInterpreter class, that could offer an API like that:
void Request(string cmdName, string[] cmdParameters);
string GetLastResult();
int GetLastCode();
Update:
Below code shows a possible object oriented solution (I've left out interfaces to make the code more compact):
using System;
using System.Collections.Generic;
using System.Linq;
namespace ConsoleApp1
{
public class Command<T>
{
public string Name { get; }
public T TheCommand { get; }
public Command(string name, T theCommand)
{
Name = name;
TheCommand = theCommand;
}
}
public interface ICommandResult
{
void Ok(Action<ICommandResult> yes, Action<ICommandResult> no);
int Code { get; }
string Description { get; }
}
public abstract class CommandResult : ICommandResult
{
public int Code { get; }
public string Description { get; }
protected CommandResult(int code, string description)
{
Code = code;
Description = description;
}
public abstract void Ok(Action<ICommandResult> yes, Action<ICommandResult> no);
}
public class NullCommandResult : CommandResult
{
public NullCommandResult() : base(-1, "null")
{
}
public override void Ok(Action<ICommandResult> yes, Action<ICommandResult> no) => no?.Invoke(this);
}
public class SuccessCommandResult : CommandResult
{
public SuccessCommandResult(string description) : base(0, description)
{
}
public override void Ok(Action<ICommandResult> yes, Action<ICommandResult> no) => yes?.Invoke(this);
}
public class CommandInterpreter
{
private Dictionary<string, Func<IEnumerable<string>, ICommandResult>> Commands = new Dictionary<string, Func<IEnumerable<string>, ICommandResult>>();
public void RegisterCommand(Command<Func<IEnumerable<string>, ICommandResult>> cmd)
=> Commands.Add(cmd.Name, cmd.TheCommand);
public ICommandResult RunCommand(string name, IEnumerable<string> parameters)
=> Commands.Where(kvp => kvp.Key.Equals(name))
.Select(kvp => kvp.Value)
.DefaultIfEmpty(strArr => new NullCommandResult())
.Single()
.Invoke(parameters);
}
class Program
{
private CommandInterpreter _cmdInterpreter;
private Program()
{
_cmdInterpreter = new CommandInterpreter();
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("pwd", PrintWorkingDirectory));
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("create", CreateFile));
_cmdInterpreter.RegisterCommand(new Command<Func<IEnumerable<string>, ICommandResult>>("print", ReadFile));
}
private static CommandResult ReadFile(IEnumerable<string> arg) => new SuccessCommandResult("File read");
private static CommandResult CreateFile(IEnumerable<string> arg) => new SuccessCommandResult("File xyz created");
private static CommandResult PrintWorkingDirectory(IEnumerable<string> arg) => new SuccessCommandResult("Printed something");
static void Main() => new Program().Run();
private void Run()
{
Console.WriteLine("Welcome, type in command.");
string input;
do
{
Console.Write("> ");
input = Console.ReadLine();
var cmdResult = _cmdInterpreter.RunCommand(input, Enumerable.Empty<string>());
cmdResult.Ok(
r => Console.WriteLine($"Success: {cmdResult.Code}, {cmdResult.Description}"),
r => Console.WriteLine($"FAILED: {cmdResult.Code}, {cmdResult.Description}"));
} while (input != "exit");
}
}
}
Output:
Welcome, type in command.
> pwd
Success: 0, Printed something
> create
Success: 0, File xyz created
> abc
FAILED: -1, null
>
You can just copy the code and play around with it.

Why isnt function executed?

I am working on creating an app just like the Output Terminal (Unix) or the Command Line Prompt (Windows). I have created a dictionary that has some keywords for a function. But when I call those keywords, nothing is done. My program is called Command Line Control (or CLC). And I use the .NET Core which is for all running software(Linux, macOS, and Windows).
I do not know what to try exactly. A function is a function. It must be executed when I call it.
using System;
using System.Collections.Generic;
using System.Threading;
using System.IO;
namespace CLC
{
class Program
{
static DirectoryInfo maindirectory;
static Dictionary<string, string> keyaction;
static string value;
static void WritePathOfWorkingDirectory(DirectoryInfo directory)
{
if (directory != null)
{
Console.Write("{0}:", directory.FullName);
}
else
{
}
}
static void ProcessAnswer(string[] array)
{
string action = array.GetValue(0).ToString();
value = array.GetValue(1).ToString();
string c = keyaction[action];
Console.Write(c);
}
static string ListFiles()
{
foreach(var file in maindirectory.GetFiles())
{
Console.WriteLine(file.Name);
}
return "ok";
}
static string ListDirectories()
{
foreach(var directory in maindirectory.GetDirectories())
{
Console.WriteLine(directory);
}
return "ok";
}
static void MainProgramm()
{
WritePathOfWorkingDirectory(maindirectory);
string data = Console.ReadLine();
var arry = data.Split(' ');
ProcessAnswer(arry);
Thread repeat = new Thread(MainProgramm);
repeat.Start();
}
static void Main(string[] args)
{
maindirectory = new DirectoryInfo("C:/Users");
keyaction = new Dictionary<string, string>();
keyaction.Add("lf", ListFiles());
keyaction.Add("ld", ListDirectories());
Console.Clear();
maindirectory = new DirectoryInfo("C:/Users");
Thread thread = new Thread(new ThreadStart(MainProgramm));
thread.Start();
}
}
}
The expected result is to do what the key says: for example if I type ld (list directories) the list directory function should be executed. But I don't get anything! The program just repeats itself.
When you include the parentheses after a Method name, you invoke the method. So rather than passing the function as the Dictionary value, you're passing the result of calling that function. You need to get a reference to the function to be called then invoke it.
Your Main method should look like this:
static void Main(string[] args)
{
maindirectory = new DirectoryInfo("C:/Users");
keyaction = new Dictionary<string, Func<string>>(); // changed this to an Func instead of a string
keyaction.Add("lf", ListFiles); // notice I removed the parentheses here
keyaction.Add("ld", ListDirectories); // and here
Console.Clear();
maindirectory = new DirectoryInfo("C:/Users");
Thread thread = new Thread(new ThreadStart(MainProgramm));
thread.Start();
}
and keyaction should be declared like this:
static Dictionary<string, Func<string>> keyaction; // a Func<string> is a function that returns a string and takes no arguments
Then, in your ProcessAnswer method you need to call the function via the reference you have to it in the Dictionary:
static void ProcessAnswer(string[] array)
{
string action = array.GetValue(0).ToString();
value = array.GetValue(1).ToString();
string c = keyaction[action](); // calling the referenced funtion
Console.Write(c);
}
This should give you the expected result.
In the current state, your program's methods only get called once and then you clear the Console before you can see the output, so it probably looks like your program just says "ok" whenever you enter a command.
The main problem is that you're storing a string value in the dictionary rather than the function itself, so when you add the functions, you're adding their immediate return value (before you ever prompt the user for input).
Instead, the dictionary value should be a Func<string>, which you can then .Invoke on demand.
Additionally, it seemed that you weren't validating the user entry. We can use TryGetValue for the user's entry to ensure that they entered a valid command (if it exists in the dictionary). If it doesn't, we can tell them their input was invalid.
Also, when accessing array values, you should first ensure that the index you're accessing is valid (if the user only enters one command, like "lf"with no value, then you can't access array index [1] because it doesn't exist.
Also, it seems like even when the user does input a path, you store it in value, but never use it. So I modified the ListFiles and ListDirectories methods to treat the value as a path, and if it exists, list the files or directories from the path specified.
Try out this code with the changes:
public class Program
{
private static DirectoryInfo _maindirectory = new DirectoryInfo("C:\\Users");
private static Dictionary<string, Func<string>> _keyaction =
new Dictionary<string, Func<string>>
{
{"lf", ListFiles},
{"ld", ListDirectories},
{"cd", ChangeDirectory},
{"exit", Exit}
};
private static string _value;
private static void DisplayPrompt(FileSystemInfo directory)
{
Console.Write($"{directory?.FullName ?? "[cmd]"}: ");
}
private static void ProcessAnswer(IReadOnlyList<string> array)
{
var action = array.Count > 0 ? array[0] : string.Empty;
_value = array.Count > 1 ? array[1] : null;
Func<string> method;
_keyaction.TryGetValue(action, out method);
if (method == null)
{
WriteError($"Unknown command: {action}");
}
else
{
Console.WriteLine(_keyaction[action].Invoke());
}
}
private static string ListFiles()
{
var dir = Directory.Exists(_value) ? new DirectoryInfo(_value) : _maindirectory;
foreach (var file in dir.GetFiles())
{
Console.WriteLine(file.Name);
}
return "ok";
}
private static string ListDirectories()
{
var dir = Directory.Exists(_value) ? new DirectoryInfo(_value) : _maindirectory;
foreach (var directory in dir.GetDirectories())
{
Console.WriteLine(directory);
}
return "ok";
}
private static string ChangeDirectory()
{
if (Directory.Exists(_value))
{
_maindirectory = new DirectoryInfo(_value);
}
else if (Directory.Exists(Path.Combine(_maindirectory.FullName, _value)))
{
_maindirectory = new DirectoryInfo(
Path.Combine(_maindirectory.FullName, _value));
}
else
{
WriteError("Directory not found.");
}
return "ok";
}
private static void WriteError(string message)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(message);
Console.ResetColor();
}
private static string Exit()
{
Environment.Exit(0);
return "ok";
}
private static void Main()
{
while (true)
{
DisplayPrompt(_maindirectory);
ProcessAnswer(Console.ReadLine()?.Split(' '));
}
}
}

C# Framework With Command Line Interface

I would like to code a framework in C# Console Application(CLI), details aren't important.
I don't know, how to recognize commands cleanly, and shortly.
I tried with switch-case:
public static void command_recognizing(string command) // random example
{
string[] tmp_array = command.Split(' ');
switch(tmp_array[0])
{
case("help"):
method_library.help(); // no need argument
break;
case("time"):
method_library.time(); // no need argument
break;
case("shutdown"):
method_library.shutdown(tmp_array); // need argument
break;
default:
Console.WriteLine("Error! {0} is not a known command!",tmp_array[0]);
break;
}
}
I also tried if-else:
public static void command_recognizing(string command) // random example
{
string[] tmp_array = command.Split(' ');
if(command.Contains("help"))
{
method_library.help(); // no need argument
}
else if(command.Contains("time"))
{
method_library.time(); // no need argument
}
else if(command.Contains("shutdown"))
{
method_library.shutdown(tmp_array); // need argument
}
else
{
Console.WriteLine("Error! {0} is not a known command!",tmp_array[0]);
}
}
I tried to store the commands in a string array, still the same, long and ugly.
There is any other way, to make the command recognizing shorter, cleaner and easier to modify?
Foregive me for my english. Feel free to correct me!
You could use Reflection to execute methods of a class.
void Main() {
var cmd = new Commands();
while (!cmd.Exitting) {
var cmdline = Console.ReadLine();
var cmdargs = Regex.Split(cmdline.Trim(), #"\s+");
if (!cmd.TryInvokeMember(cmdargs[0], cmdargs.Skip(1).ToArray()))
Console.WriteLine($"Unknown command: {cmdargs[0]}");
}
}
// Define other methods and classes here
public class Commands {
public bool Exitting { get; private set; }
public Commands() {
Exitting = false;
}
public void exit() {
Exitting = true;
}
public int sum(object[] args) {
return args.Select(s => Convert.ToInt32(s)).Sum();
}
public bool TryInvokeMember(string methodName, object[] args) {
var method = typeof(Commands).GetMethod(methodName.ToLower());
if (method != null) {
object res;
if (method.GetParameters().Length > 0)
res = method.Invoke(this, new object[] { args });
else
res = method.Invoke(this, new object[0]);
if (method.ReturnType != typeof(void))
Console.WriteLine(res.ToString());
return true;
}
else
return false;
}
}

Mono.Cecil: Insert a log statement in method's beginning

I am using Mono.Cecil to edit my target method's IL code so that I can log that method's entry point, without editing the actual code.
I am able to insert a call instruction to a method which can perform logging operation.
But I don't know how to log my target method's input parameters.
In short i want to insert an instruction in the target method by changing it's IL code to do a log or say print operation to log the input parameter values passed to that method.
I tried a basic program as sample.
public class Target
{
// My target method.
public void Run(int arg0, string arg1)
{
Console.WriteLine("Run method body");
}
}
public static class Trace{
// This is my log method, which i want to call in begining of Run() method.
public void LogEntry(string methodName, object[] params)
{
System.Console.WriteLine("******Entered in "+ methodName+" method.***********")
// With params :......
//
}
}
Source program.
public class Sample
{
private readonly string _targetFileName;
private readonly ModuleDefinition _module;
public ModuleDefinition TargetModule { get { return _module; } }
public Sample(string targetFileName)
{
_targetFileName = targetFileName;
// Read the module with default parameters
_module = ModuleDefinition.ReadModule(_targetFileName);
}
public void Run(string type, string method)
{
// Retrive the target class.
var targetType = _module.Types.Single(t => t.Name == type);
// Retrieve the target method.
var runMethod = targetType.Methods.Single(m => m.Name == method);
// Get a ILProcessor for the Run method
var processor = runMethod.Body.GetILProcessor();
// get log entry method ref to create instruction
var logEntryMethodReference = targetType.Methods.Single(m => m.Name == "LogEntry");
// Import ..
//
var newInstruction = processor.Create(OpCodes.Call, logEntryMethodReference);
var firstInstruction = runMethod.Body.Instructions[0];
processor.InsertBefore(firstInstruction, newInstruction);
// Write the module with default parameters
_module.Write(_targetFileName);
}
}
Well, this was interesting :)
Here's my working sample (comments in the code, feel free to ask anything, if not clear):
Modified sample (to actually write out the parameters):
public class Target
{
// My target method.
public void Run(int arg0, string arg1)
{
Console.WriteLine("Run method body");
}
}
public static class Trace
{
// This is my log method, which i want to call in begining of Run() method.
public static void LogEntry(string methodName, object[] parameters)
{
Console.WriteLine("******Entered in " + methodName + " method.***********");
Console.WriteLine(parameters[0]);
Console.WriteLine(parameters[1]);
}
}
Source program to handle IL injection:
public class Sample
{
private readonly string _targetFileName;
private readonly ModuleDefinition _module;
public ModuleDefinition TargetModule { get { return _module; } }
public Sample(string targetFileName)
{
_targetFileName = targetFileName;
// Read the module with default parameters
_module = ModuleDefinition.ReadModule(_targetFileName);
}
public void Run(string type, string method)
{
// Retrive the target class.
var targetType = _module.Types.Single(t => t.Name == type);
// Retrieve the target method.
var runMethod = targetType.Methods.Single(m => m.Name == method);
// Get a ILProcessor for the Run method
// get log entry method ref to create instruction
var logEntryMethodReference = _module.Types.Single(t => t.Name == "Trace").Methods.Single(m => m.Name == "LogEntry");
List<Instruction> newInstructions = new List<Instruction>();
var arrayDef = new VariableDefinition(new ArrayType(_module.TypeSystem.Object)); // create variable to hold the array to be passed to the LogEntry() method
runMethod.Body.Variables.Add(arrayDef); // add variable to the method
var processor = runMethod.Body.GetILProcessor();
newInstructions.Add(processor.Create(OpCodes.Ldc_I4, runMethod.Parameters.Count)); // load to the stack the number of parameters
newInstructions.Add(processor.Create(OpCodes.Newarr, _module.TypeSystem.Object)); // create a new object[] with the number loaded to the stack
newInstructions.Add(processor.Create(OpCodes.Stloc, arrayDef)); // store the array in the local variable
// loop through the parameters of the method to run
for (int i = 0; i < runMethod.Parameters.Count; i++)
{
newInstructions.Add(processor.Create(OpCodes.Ldloc, arrayDef)); // load the array from the local variable
newInstructions.Add(processor.Create(OpCodes.Ldc_I4, i)); // load the index
newInstructions.Add(processor.Create(OpCodes.Ldarg, i+1)); // load the argument of the original method (note that parameter 0 is 'this', that's omitted)
if (runMethod.Parameters[i].ParameterType.IsValueType)
{
newInstructions.Add(processor.Create(OpCodes.Box, runMethod.Parameters[i].ParameterType)); // boxing is needed for value types
}
else
{
newInstructions.Add(processor.Create(OpCodes.Castclass, _module.TypeSystem.Object)); // casting for reference types
}
newInstructions.Add(processor.Create(OpCodes.Stelem_Ref)); // store in the array
}
newInstructions.Add(processor.Create(OpCodes.Ldstr, method)); // load the method name to the stack
newInstructions.Add(processor.Create(OpCodes.Ldloc, arrayDef)); // load the array to the stack
newInstructions.Add(processor.Create(OpCodes.Call, logEntryMethodReference)); // call the LogEntry() method
foreach (var newInstruction in newInstructions.Reverse<Instruction>()) // add the new instructions in referse order
{
var firstInstruction = runMethod.Body.Instructions[0];
processor.InsertBefore(firstInstruction, newInstruction);
}
// Write the module with default parameters
_module.Write(_targetFileName);
}
}

Creating A Command Based Application Without Extensive Use Of If-Else Statements

I have this C# application i want to work on. It is mostly command based and what I mean with this is that the user enters some type of command and the application simply performs some kind of task.
For example: User can type in a command such as getdate, the application reads this command and simply displays a date.
NOTE: Its going to be console based but my problem, the actual application has about 80 to 100 commands and my question is how to read this command without relying on some verbose if-else statement to check which command was typed in.
Is there a way I can do this or I just have to go with some long if-else statements.
There are several options you could take:
You could have a hastable of the commands that map to the type to initialize.
Dictionary<string, Type> Where type maps to the class you initialize.
Use Reflection to directly map the command to an object to initialize (via object name or an attribute.
[Command(Name = "update")]
public class RunUpdates : ICommand {
}
[Command(Name = "restart")]
public class RestartServer : ICommand {
}
Then use reflection to find the object that implements ICommand with the attribute matching the command name.
Use a simple form of Command Pattern along with some kind of Command Collection.
A simple way to build this would be:
public class MyApp
{
private readonly Dictionary<string, Action> _commands = new Dictionary<string, Action>();
public static void Main()
{
var program = new MyApp();
// Run Console Stuff
}
public MyApp()
{
SetupCommands();
}
public void SetupCommands()
{
_commands.Add("PrintDate", () => Console.WriteLine(DateTime.Now));
_commands.Add("PrintMaxOf3And7", () => Console.WriteLine(Math.Max(3, 7)));
}
public void ExecuteCommand(string commandName)
{
_commands[commandName].Invoke();
}
}
Use delegate:
public delegate void CommandDelegate(string input);
public Dictionary<string, CommandDelegate> Commands { get; set; }
/// usage
public void InitCommands()
{
Commands.Add("showdata", (input) => Console.WriteLine(....));
... // other commands
}
public void ExecuteCommand(string userInput)
{
var firstWord = userInput.Substring(0, userInput.IndexOf(" "));
if (Commands.ContainsKey(firstWord))
{
var command = Commands[firstWord];
command(userInput);
}
}
You could use a dictionary that uses string as keys and methods (either Action, Func or a custom delegate) as value, then you just need to reas the input from the user and use it a key key to get the corresponding action. If the command can have parameters like this command param1 then use string.Split to separate the command from the parameter, then use the command string as key, and when you execute the method pass the other string as parameter (depending of the type of data of the parameter to may need to parse the parameter of the command from string to something else)
The code would look like this:
Using Func:
NOTE: Func requires at least one parameter and a return value.
void Main()
{
public Dictionary<string, Func<string, int>> commands =
new Dictionary<string, Func<string, int>>();
commands.Add("getdate", GetDate);
Console.WriteLine("Enter a command");
string input = Console.ReadLine(); //<-- Try typing "getdate"
commands[input].Invoke();
}
public int GetDate(string someParameter)
{
Console.WriteLine(DateTime.Today);
return 0;
}
Using Action:
NOTE: Action requires at least one parameter.
void Main()
{
public Dictionary<string, Action<string>> commands = new Dictionary<string, Action>();
commands.Add("getdate", GetDate);
Console.WriteLine("Enter a command");
string input = Console.ReadLine(); //<-- Try typing "getdate"
commands[input].Invoke();
}
public void GetDate(string someParameter)
{
Console.WriteLine(DateTime.Today);
}
Using Custom Delegate:
public delegate double YourDelegate(string param);
void Main()
{
public Dictionary<string, YourDelegate> commands =
new Dictionary<string, YourDelegate>();
commands.Add("getdate", GetDate);
Console.WriteLine("Enter a command");
string input = Console.ReadLine(); //<-- Try typing "getdate"
commands[input].Invoke();
}
public double GetDate(string someParameter)
{
Console.WriteLine(DateTime.Today);
return 0.0;
}
You could use switch, or you could create a Dictionary with question as key and command as value. Suggest you do a ToLower() on both key and input to make it case insensitive, as relying on user to type perfectly will be difficult.
private Dictionary<string,object> commandList = new Dictionary<string,object>();
private void processCommand(){
commandList.Add("showdate",DateTime.Now);
string command = Console.ReadLine();
if(command.Length>0){
if(commandList.ContainsKey(command);
object o = commandList[command.ToLower()];
//do something
}
}
}

Categories

Resources