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
}
}
}
Related
I have an enum, of Actions, I want to run:
public enum theActions
{
action1,
action2
}
I want to store them, in a Dictionary:
public Dictionary<theActions, Action> _theActions { get; }
_theActions = new Dictionary<theActions, Action>
{
[theActions.action1] = () => action1Func()
};
I'd have my functions, for each action(s):
public void action1Func(int inParam)
{
//do whatever
}
Later, I'd need to call one of the functions:
public void execAction(int inVar, Action action)
{
//inVar isn't the parameter I want to pass to the action. It's used, for something else.
action();
}
execAction(1, _theActions[theActions.action1]);
I'm not sure, how to change my code to make the Action take parameters everywhere and what if I need one action which doesn't need a parameter? Do I have to add a dummy parameter, in that function?
I got this, so far:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class Form1 : Form
{
public enum theActions
{
action1,
action2
}
public Dictionary<theActions, Action<int>> _theActions { get; }
public void execAction(int inVar, Action<int> action)
{
//inVar isn't the parameter I want to pass to the action. It's used, for something else.
// action();
}
public Form1()
{
InitializeComponent();
_theActions = new Dictionary<theActions, Action<int>>
{
[theActions.action1] = (Action<int>)((int x) => action1Func(x))
};
}
public void action1Func(int inParam)
{
//do whatever
MessageBox.Show($"Hello ... inParam : {inParam}");
}
private void button1_Click(object sender, EventArgs e)
{
//This works manually
_theActions[theActions.action1].Invoke(12);
//But, I want the execAction to work
//execAction(1, _theActions[theActions.action1]);
}
}
}
It works manually calling it. I just need help getting into execAction() and running it. So, close.
public void execAction(int someInt, Action action)
{
action();
// or: action.Invoke();
}
You should be able to remove the whole lambda in the initialization as you already have a method with one int parameter right there:
public Form1()
{
InitializeComponent();
_theActions = new Dictionary<theActions, Action<int>>
{
[theActions.action1] = action1Func
};
}
The call with a specific parameter would look like:
int yourParameter = 12345;
execAction(42, () => (_theActions[theActions.action1]).Invoke(yourParameter));
Try to drop the dictionary and use this instead:
public void action1Func(int p1) //action 1 has one int parameter
{
}
public void action2Func() //let's consider action 2 takes no parameters
{
}
public void action3Func(int p1, int p2, int p3)
{
}
// Order of parameters was changed to leverage implicit null parameters for convenience
// See invocations below
public void execAction(theActions action,
int? inVar1 = null, int? inVar2 = null, int? inVar3 = null)
{
switch (action) // Based on action kind, invoke the right function.
{ // The mapping is now coded in a switch statement
// instead of a Dictionary declaration
case theActions.action1: action1Func(inVar1.Value); break;
case theActions.action2: action2Func(); break;
case theActions.action3: action3Func(inVar1.Value, inVar2.Value, inVar3.Value); break;
}
}
// Invocations
execAction(theActions.action1, 1);
execAction(theActions.action2);
execAction(theActions.action3, 1, 3, 5);
You can call methods with arbitrary number of parameters if you use the implicit parameter declaration correctly for execAction.
You can perform parameter validation (e.g. above you can check that inVar1.HasValue == true before invoking) otherwise the code will fail fast if you omit a parameter (Nullable.Value throws InvalidOperationException if HasValue is false).
If number of parameters grows and becomes unmanageable you can put them in a parameters bag class and validate their initialization via constructor.
You can get more safety (compile time checks) if you define these overloads:
public void execAction1(int p1)
=> execAction(theActions.action1, p1);
public void execAction2()
=> execAction(theActions.action2);
public void execAction3(int p1, int p2, int p3)
=> execAction(theActions.action3, p1, p2, p3);
// Invocations will be
execAction1(1);
execAction2();
execAction3(1, 3, 5);
but this last step kind of defeats the purpose. You can invoke the original methods instead :).
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.
I have created this helper class RichTextBoxHelper that has an extension method, and I would like to write another WriteLine method or rewrite this one (which solution is best) in order to be able to use it in the function presented under it. Thank you.
public static class RichTextBoxHelper
{
public static void WriteLine(this RichTextBox txtLog, object line)
{
txtLog.AppendText(line + Environment.NewLine);
}
}
private void selectToolStripMenuItem_Click(object sender, EventArgs e)
{
var vehicles = new List<Tuple<string, string, int>>
{
Tuple.Create("123","VW",1999),
Tuple.Create("234","Ford",2009),
Tuple.Create("567","Audi",2005),
Tuple.Create("678","Ford",2003),
Tuple.Create("789","Mazda",2003),
Tuple.Create("999","Ford",1965)
};
var fordCars = vehicles.Where(v => v.Item2 == "Ford")
.Select(v => new Car
{
VIN = v.Item1,
Make = v.Item2,
Year = v.Item3
});
foreach (var item in fordCars)
txtLog.WriteLine("Car VIN:{0} Make:{1} Year:{2}", item.VIN, item.Make, item.Year);
}
Yep, that's completely possible. It's called method overloading and it works just as well on extension method classes as normal classes.
The signature you require for your new method is:
public static void WriteLine(
this RichTextBox txtLog,
string format,
params object[] args)
{
// ...
}
Just put it in the same class as your other one and you'll be able to use both as appropriate.
Alternatively you can call your existing method in the following way:
txtLog.WriteLine(
String.Format(
"Car VIN:{0} Make:{1} Year:{2}",
item.VIN,
item.Make,
item.Year));
I think dav_i answer is correct but I prefer you to write your extension method for IsIn method something like below, because you can use it everywhere for every different kind of variables:
public static class ExtensionMethods
{
public static bool IsIn<T>(this T keyObject, params object[] collection)
{
return collection.Contains(keyObject);
}
}
usage of method is like here:
if (intValue.IsIn( 2, 3, 7 ))
{
do something...
}
if (stringVlaue.IsIn("a","b","c"))
{
do something...
}
I would like to be able to get a list of parameters in the form of a IDictionary<string, object> of the previous method called. There is one catch: I cannot make use of third-party Aspect Oriented Programming framework even when it's free.
For example:
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Question {
internal class Program {
public static void Main(string[] args) {
var impl = new Implementation();
impl.MethodA(1, "two", new OtherClass { Name = "John", Age = 100 });
}
}
internal class Implementation {
public void MethodA(int param1, string param2, OtherClass param3) {
Logger.LogParameters();
}
}
internal class OtherClass {
public string Name { get; set; }
public int Age { get; set; }
}
internal class Logger {
public static void LogParameters() {
var parameters = GetParametersFromPreviousMethodCall();
foreach (var keyValuePair in parameters)
Console.WriteLine(keyValuePair.Key + "=" + keyValuePair.Value);
// keyValuePair.Value may return a object that maybe required to
// inspect to get a representation as a string.
}
private static IDictionary<string, object> GetParametersFromPreviousMethodCall() {
throw new NotImplementedException("I need help here!");
}
}
}
Any suggestion or ideas? Feel free to use reflection if necessary.
You could use StackTrace to get all you need:
var trace = new System.Diagnostics.StackTrace();
var frame = trace.GetFrame(1); //previous
var method = frame.GetMethod();
Now you have a MethodBase instance.
You could get the Name by:
var method = method.Name;
and the parameters by MethodBase.GetParameters.
For example:
var dict = new Dictionary<string, object>();
foreach (var param in method.GetParameters())
{
dict.Add(param.Name, param.DefaultValue);
}
I think the best you can do without AOP is use the StackFrame and get the method that was called.
I imagine this would require way too much overhead. What if you passed in a variable that you modified? You would have to allocate extra space just to store the original value before it was modified within the method. That could get out of hand really quick
I want enqueue a list of tasks and then perform on certain event. Code:
internal class MyClass
{
private Queue<Task> m_taskQueue;
protected MyClass()
{
m_taskQueue = new Queue<Task>();
}
public delegate bool Task(object[] args);
public void EnqueueTask(Task task)
{
m_taskQueue.Enqueue(task);
}
public virtual bool Save()
{
// save by processing work queue
while (m_taskQueue.Count > 0)
{
var task = m_taskQueue.Dequeue();
var workItemResult = task.Invoke();
if (!workItemResult)
{
// give up on a failure
m_taskQueue.Clear();
return false;
}
}
return true;
}
}
Each delegate task may have their own list of parameters: Task(object[] args). My question is how to pass the parameter to each task for the task queue?
Okay, now we have a bit more information, it sounds like your EnqueueTask method should actually look like this:
public void EnqueueTask(Task task, object[] values)
Right?
For starters I would avoid using the name Task, which is already part of the core of .NET 4 and will become very prominent in .NET 5. As Joshua said, you've basically got a Func<object[], bool>.
Next, you could keep two lists - one for the delegates and one for the values, but it's easier just to keep a Queue<Func<bool>> like this:
private readonly Queue<Func<bool>> taskQueue = new Queue<Func<bool>>();
public void EnqueueTask(Task task, object[] values)
{
taskQueue.Enqueue(() => task(values));
}
Then the rest of your code will actually work "as is". The lambda expression there will capture values and task, so when you invoke the Func<bool>, it will supply those values to the original delegate.
Provided understanding your question correctly you just pass the information like a normal call. Have you considered using Func? You can just pass arguments to the Task.Invoke i.e. Task.Invoke([arguments here as a *single* object array]).
object[] arguments = null; // assign arguments to something
var workItemResult = task.Invoke(arguments);
Below is an example with the Func type.
internal class MyClass
{
private Queue<Func<object[], bool>> m_taskQueue;
protected MyClass()
{
m_taskQueue = new Queue<Func<object[], bool>>();
}
public void EnqueueTask(Func<object[], bool> task)
{
m_taskQueue.Enqueue(task);
}
public virtual bool Save()
{
object[] arguments = null; // assign arguments to something
// save by processing work queue
while (m_taskQueue.Count > 0)
{
var task = m_taskQueue.Dequeue();
var workItemResult = task(arguments);
if (!workItemResult)
{
// give up on a failure
m_taskQueue.Clear();
return false;
}
}
return true;
}
}