snippetcsharpMinor
how to avoid switch-case
Viewed 0 times
caseswitchavoidhow
Problem
How can I refactor this code to avoid switch-cases? Is there a way of changing commands without adding new case and make it dynamically...I want the solution not to depend on the amount of cases. For example, if I have 1000 commands..I should obviously write them all as cases?
I hope my problem is clear.
```
static void Main(string[] args)
{
ConsoleHelp ch1 = new ConsoleHelp();
Plus pl = new Plus();
Minus mn = new Minus();
Divide dv = new Divide();
Multiply mlt = new Multiply();
Sinus sin = new Sinus();
Tangent tan = new Tangent();
Square sq = new Square();
Degree dg = new Degree();
Percent pr = new Percent();
while (true)
{
Console.Write("command> ");
string com = Console.ReadLine();
if (com != null)
{
switch (com.ToLower())
{
case "?":
ch1.Helpper();
break;
case "plus":
pl.Sum();
break;
case "minus":
mn.Minusvalue();
break;
case "divide":
dv.Dividevalue();
break;
case "multiply":
mlt.Multvalue();
break;
case "sinus":
sin.Sinusvalue();
break;
case "tangent":
tan.Tangentvalue();
break;
case "square":
sq.Squarevalue();
break;
case "degree":
dg.Degvalue();
break;
case "percent":
pr.Pervalue();
break;
case "c":
Environment.Exit(0);
break;
default:
Console.WriteLine("The command is not supported. Enter one of the valid comman
I hope my problem is clear.
```
static void Main(string[] args)
{
ConsoleHelp ch1 = new ConsoleHelp();
Plus pl = new Plus();
Minus mn = new Minus();
Divide dv = new Divide();
Multiply mlt = new Multiply();
Sinus sin = new Sinus();
Tangent tan = new Tangent();
Square sq = new Square();
Degree dg = new Degree();
Percent pr = new Percent();
while (true)
{
Console.Write("command> ");
string com = Console.ReadLine();
if (com != null)
{
switch (com.ToLower())
{
case "?":
ch1.Helpper();
break;
case "plus":
pl.Sum();
break;
case "minus":
mn.Minusvalue();
break;
case "divide":
dv.Dividevalue();
break;
case "multiply":
mlt.Multvalue();
break;
case "sinus":
sin.Sinusvalue();
break;
case "tangent":
tan.Tangentvalue();
break;
case "square":
sq.Squarevalue();
break;
case "degree":
dg.Degvalue();
break;
case "percent":
pr.Pervalue();
break;
case "c":
Environment.Exit(0);
break;
default:
Console.WriteLine("The command is not supported. Enter one of the valid comman
Solution
You can create your own Attribute to decorate all of your classes (e.g., Plus, ConsoleHelp, Minus, Divide, and even Exit). In the sample below, the attribute is called
The next step would be to have each of your command classes implement the same interface. In the example code I used the name
Then you can use reflection to search for all classes that have that attribute and load them into a dictionary of
From there you can use the dictionary instead of the switch statement.
To add a new command in the future, all you would need to do would be to
Your main program would not need to change at all.
SystemCommandAttribute. It would require to specify what the name of the command would be as a constructor parameter (what the user would have to type on the command line). The next step would be to have each of your command classes implement the same interface. In the example code I used the name
ISystemCommand. That interface should have a single method: Execute(). That way we know what to call when we want to invoke the command.Then you can use reflection to search for all classes that have that attribute and load them into a dictionary of
string to ISystemCommand. The GetCommandsFromAssemblies() method handles instantiating each instance of ISystemCommand for you. From there you can use the dictionary instead of the switch statement.
public class Program
{
static void Main(string[] args)
{
Dictionary commandToType = GetCommandsFromAssemblies();
RunCommandLoop(commandToType);
}
///
/// Loop over all types currently loaded in the AppDomain looking for ISystemCommand
///
/// A dictionary from command name to instance of command
private static Dictionary GetCommandsFromAssemblies()
{
var typesWithMyAttribute =
from a in AppDomain.CurrentDomain.GetAssemblies()
from t in a.GetTypes()
let attributes = t.GetCustomAttributes(typeof(SystemCommandAttribute), true)
where attributes != null && attributes.Length > 0
select new
{
Instance = (ISystemCommand)Activator.CreateInstance(t),
CommandName = attributes.Cast().Single().CommandName
};
Dictionary commandToType = typesWithMyAttribute.ToDictionary(t => t.CommandName, t => t.Instance);
return commandToType;
}
private static void RunCommandLoop(Dictionary actions)
{
while (true)
{
Console.Write("command> ");
string com = Console.ReadLine();
if (com != null)
{
ISystemCommand action;
if (actions.TryGetValue(com, out action))
{
action.Execute();
}
else
{
Console.WriteLine("The command is not supported. Enter one of the valid commands.");
break;
}
}
}
Console.ReadLine();
}
}
public interface ISystemCommand
{
void Execute();
}
public class SystemCommandAttribute : Attribute
{
public SystemCommandAttribute(string commandName)
{
CommandName = commandName;
}
public string CommandName { get; set; }
}
[SystemCommand("plus")]
public class Plus : ISystemCommand
{
public void Execute()
{
Sum();
}
private void Sum()
{
// your existing Sum logic here
Console.WriteLine("Sum called");
}
}
[SystemCommand("?")]
public class ConsoleHelp : ISystemCommand
{
public void Execute()
{
Helper();
}
private void Helper()
{
// your existing Helper logic here
Console.WriteLine("Helper called");
}
}
[SystemCommand("c")]
public class ExitCommand : ISystemCommand
{
public void Execute()
{
Environment.Exit(0);
}
}To add a new command in the future, all you would need to do would be to
- create your command class
- Inherit from ISystmeCommand
- Add the SystmCommandAttribute
Your main program would not need to change at all.
Code Snippets
public class Program
{
static void Main(string[] args)
{
Dictionary<string, ISystemCommand> commandToType = GetCommandsFromAssemblies();
RunCommandLoop(commandToType);
}
/// <summary>
/// Loop over all types currently loaded in the AppDomain looking for ISystemCommand
/// </summary>
/// <returns>A dictionary from command name to instance of command</returns>
private static Dictionary<string, ISystemCommand> GetCommandsFromAssemblies()
{
var typesWithMyAttribute =
from a in AppDomain.CurrentDomain.GetAssemblies()
from t in a.GetTypes()
let attributes = t.GetCustomAttributes(typeof(SystemCommandAttribute), true)
where attributes != null && attributes.Length > 0
select new
{
Instance = (ISystemCommand)Activator.CreateInstance(t),
CommandName = attributes.Cast<SystemCommandAttribute>().Single().CommandName
};
Dictionary<string, ISystemCommand> commandToType = typesWithMyAttribute.ToDictionary(t => t.CommandName, t => t.Instance);
return commandToType;
}
private static void RunCommandLoop(Dictionary<string, ISystemCommand> actions)
{
while (true)
{
Console.Write("command> ");
string com = Console.ReadLine();
if (com != null)
{
ISystemCommand action;
if (actions.TryGetValue(com, out action))
{
action.Execute();
}
else
{
Console.WriteLine("The command is not supported. Enter one of the valid commands.");
break;
}
}
}
Console.ReadLine();
}
}
public interface ISystemCommand
{
void Execute();
}
public class SystemCommandAttribute : Attribute
{
public SystemCommandAttribute(string commandName)
{
CommandName = commandName;
}
public string CommandName { get; set; }
}
[SystemCommand("plus")]
public class Plus : ISystemCommand
{
public void Execute()
{
Sum();
}
private void Sum()
{
// your existing Sum logic here
Console.WriteLine("Sum called");
}
}
[SystemCommand("?")]
public class ConsoleHelp : ISystemCommand
{
public void Execute()
{
Helper();
}
private void Helper()
{
// your existing Helper logic here
Console.WriteLine("Helper called");
}
}
[SystemCommand("c")]
public class ExitCommand : ISystemCommand
{
public void Execute()
{
Environment.Exit(0);
}
}Context
StackExchange Code Review Q#29664, answer score: 7
Revisions (0)
No revisions yet.