Reputation: 1891
I'm trying to find a solution for this problem. This is my example code:
class Program
{
private string Command;
private static string[] Commands = { "ComandOne", "CommandTwo", "CommandThree", "CommandFour" };
static void Main(string[] args)
{
Command = args[0];
switch(Command)
{
case Commands[0]: //do something
break;
case Commands[1]: //do something else
break;
case Commands[2]: //do something totally different
break;
case Commands[3]: //do something boring
break;
default: //do your default stuff
break;
}
}
void DifferentMethod()
{
foreach(string c in Commands)
{
//do something funny
}
}
}
This code doesn't work because the string values in the switch are not constants. I want to write easy maintainable code.
I like to use something like an array because I need to use the same values somewhere else in a loop.
With int-values an enum would be perfect, but I didn't find a small solution for the same thing with strings.
Upvotes: 11
Views: 16644
Reputation: 77546
Convert Commands
into an enum:
enum Commands { ComandOne, CommandTwo, CommandThree, CommandFour }
Switch statement should look like:
static void Main(string[] args)
{
Command = (Commands)Enum.Parse(typeof(Commands), args[0]);
switch(Command)
{
case Commands.CommandOne:
//do something
break;
case Commands.CommandTwo:
//do something else
break;
...
default:
// default stuff
}
}
And your last method should look like:
void DifferentMethod()
{
foreach(var c in Enum.GetValues(typeof(Commands)))
{
string s = c.ToString();
//do something funny
}
}
Upvotes: 19
Reputation: 2462
I generally dislike strings for this sort of thing - it's too easy to get into trouble with misspellings, different casings and the like - but presumably that's why you want to use a variable instead of string literal. If the enum solutions aren't suitable, using consts should accomplish your goal.
EDIT: Oct 28, 2013 to fix an incorrect assignment
class Program
{
private string Command;
const string command1 = "CommandOne";
const string command2 = "CommandTwo";
const string command3 = "CommandThree";
const string command4 = "CommandFour";
private static string[] Commands = { command1, command2, command3, command4 };
static void Main(string[] args)
{
string Command = args[0];
switch (Command)
{
case command1: //do something
break;
case command2: //do something else
break;
case command3: //do something totally different
break;
case command4: //do something boring
break;
default: //do your default stuff
break;
}
}
void DifferentMethod()
{
foreach (string c in Commands)
{
//do something funny
}
}
}
Upvotes: 3
Reputation: 48949
You could eliminate the switch
statement altogether by creating IYourCommand
objects and loading them into a Dictionary<string, IYourCommand>
.
class Program
{
private Dictionary<string, IYourCommand> Command = new Dictionary<string, IYourCommand>
{
{ "CommandOne", new CommandOne() },
{ "CommandTwo", new CommandTwo() },
{ "CommandThree", new CommandThree() },
{ "CommandFour", new CommandFour() },
};
public static void Main(string[] args)
{
if (Command.ContainsKey(args[0]))
{
Command[args[0]].DoSomething();
}
}
}
public interface IYourCommand
{
void DoSomething();
}
Upvotes: 5
Reputation: 5442
Great answers here and probably answer your question better than what I'm going to mention...
Depending on how complicated your logic is based, you may consider using a strategy pattern like this:
Refactoring a Switch statement
or
Again, most likely more complicated than your solution asked, just throwing it out there...
Upvotes: 1
Reputation: 4042
As you said, only constant expressions are allowed in a switch. You would normally do this by defining an enum
and use that in your switch.
class Program
{
private enum Command
{
CommandOne = 1,
CommandTwo = 2,
CommandThree = 3
}
static void Main(string[] args)
{
var command = Enum.Parse(typeof(Commands), args[0]);
switch(command )
{
case Command.CommandOne: //do something
break;
case Command.CommandTwo: //do something else
break;
case Command.CommandThree: //do something totally different
break;
default: //do your default stuff
break;
}
}
}
Use Enum.GetValues
to enumerate through enum values in DifferentMethod
.
Upvotes: 2
Reputation: 113402
An easy fix in your specific example:
switch(Array.IndexOf(Commands, Command))
{
case 0: ...
case 1: ...
default: //unknown command. Technically, this is case -1
}
Other alternatives:
Inline the strings.
switch(Command) { case "CommandOne": ... case "CommandTwo": ... }
Use an enumeration instead, as KirkWoll says. This is probably the cleanest solution.
In more complex scenarios, using a lookup such as Dictionary<string, Action>
or Dictionary<string, Func<Foo>>
might provide better expressibility.
If the cases are complex, you could create an ICommand
interface. This will require mapping the command-string to the right concrete-implementation, for which you use simple constructs (switch / dictionaries) or fancy reflection (find ICommand
implementations with that name, or with a certain attribute decoration).
Upvotes: 8
Reputation: 1462
You can do it the other way around and reach your objective.
Use Enum and its GetNames call to get a string array to loop through.
Enum.GetNames(typeof (*YOURENUM*));
For more info. http://msdn.microsoft.com/en-us/library/system.enum.getnames.aspx
Upvotes: 1
Reputation: 54138
Define a Dictionary<string, enum>
and map the input command to the appropriate value before entering the switch. If match is not found, then default processing happens.
Upvotes: 2
Reputation: 60065
Just yesterday i created a solution for it. In your case enum
s are better but here is my solution for general non-const switch-case situation.
usages:
static string DigitToStr(int i)
{
return i
.Case(1, "one")
.Case(2, "two")
.Case(3, "three")
.Case(4, "four")
.Case(5, "five")
.Case(6, "six")
.Case(7, "seven")
.Case(8, "eight")
.Case(9, "nine")
.Default("");
}
int a = 1, b = 2, c = 3;
int d = (4 * a * c - b * 2);
string res = true
.Case(d < 0, "No roots")
.Case(d == 0, "One root")
.Case(d > 0, "Two roots")
.Default(_ => { throw new Exception("Impossible!"); });
string res2 = d
.Case(x => x < 0, "No roots")
.Case(x => x == 0, "One root")
.Case(x => x > 0, "Two roots")
.Default(_ => { throw new Exception("Impossible!"); });
string ranges = 11
.Case(1, "one")
.Case(2, "two")
.Case(3, "three")
.Case(x => x >= 4 && x < 10, "small")
.Case(10, "ten")
.Default("big");
definition:
class Res<O, R>
{
public O value;
public bool succ;
public R result;
public Res()
{
}
static public implicit operator R(Res<O, R> v)
{
if (!v.succ)
throw new ArgumentException("No case condition is true and there is no default block");
return v.result;
}
}
static class Op
{
static public Res<O, R> Case<O, V, R>(this Res<O, R> o, V v, R r)
{
if (!o.succ && Equals(o.value, v))
{
o.result = r;
o.succ = true;
}
return o;
}
static public Res<O, R> Case<O, V, R>(this O o, V v, R r)
{
return new Res<O, R>()
{
value = o,
result = r,
succ = Equals(o, v),
};
}
static public Res<O, R> Case<O, R>(this Res<O, R> o, Predicate<O> cond, R r)
{
if (!o.succ && cond(o.value))
{
o.result = r;
o.succ = true;
}
return o;
}
static public Res<O, R> Case<O, R>(this O o, Predicate<O> cond, R r)
{
return new Res<O, R>()
{
value = o,
result = r,
succ = cond(o),
};
}
private static bool Equals<O, V>(O o, V v)
{
return o == null ? v == null : o.Equals(v);
}
static public R Default<O, R>(this Res<O, R> o, R r)
{
return o.succ
? o.result
: r;
}
static public R Default<O, R>(this Res<O, R> o, Func<O, R> def)
{
return o.succ ? o.result : def(o.value);
}
}
Upvotes: 5