问题
In my program I have a listbox that when the user double clicks an object it looks to a switch statement to see what event should occur. As the list begins getting larger I'm curious if there is a way to avoid having to maintain the list of objects in 2 places (once in a list to Add to the listbox, and once in the switch statement. Is there a way to index/read/store the various Cases of my switch statement, then add them as objects to my listbox?
Example: (doesn't work, just a theory)
Switch (n)
ForEach (Case c in Cases)
{
arrayCases.Add(c);
}
listbox.Items.AddRange(arrayCases);
EDIT:
Going on the Dictionary recommendations I now have:
public void SetDictionary()
{
//add entries to the dictionary
dict["cat"] = new Action(Cat);
dict["dog"] = new Action(Dog);
//add each dictionary entry to the listbox.
foreach (string key in dict.Keys)
{
listboxTest.Items.Add(key);
}
}
//when an item in the listbox is double clicked
private void listboxTest_DoubleClick(object sender, EventArgs e)
{
testrun(listboxCases.SelectedItem.ToString());
}
public void testrun(string n)
{
//this is supposed to receive the item that was double clicked in the listbox, and run it's corresponding action as defined in the dictionary.
var action = dict[n] as Action action();
}
I believe that my code above is mostly correct and that I'm understanding it, however the action line: var action = dict[n] as Action action();
Shows an error stating 'action' is expecting a ';'. Is my logic here accurate? If so, why is the action call incorrect?
回答1:
Dictionary<string, Action>
is the way to avoid. Dictionary.Keys
becomes ListBox.Items
.
switch(n)
becomes
var action = dict[n] as Action
action();
回答2:
I suggest to move your operations into separate classes. Create a base class for your operations like the following one. I added a field for the form because you probably have to interact with your form. You can also pass in other objects if required.
internal abstract class Operation
{
protected readonly MyForm form = null;
protected Operation(MyForm form)
{
this.form = form;
}
public abstract String DisplayName { get; }
internal abstract void Execute();
}
Then derive one class for each operation.
internal sealed class DoThis : Operation
{
internal DoThis(MyForm form) : base(form) { }
public override String DisplayName
{
get { return "Do this!"; }
}
internal override void Execute()
{
// Code to do this. You can use this.form to interact with
// your form from this operation.
}
}
internal sealed class DoSomethingElse : Operation
{
internal DoSomethingElse(MyForm form) : base(form) { }
public override String DisplayName
{
get { return "Do something else!"; }
}
internal override void Execute()
{
// Code to do something else.
}
}
Now you can add all your operations to the list box
this.lsitBox.Items.Add(new DoThis(this));
this.lsitBox.Items.Add(new DoSomethingElse(this));
and set the display member property.
this.listBox.DisplayMember = "DisplayName";
Finally execute the selected operation in the event handler.
((Operation)this.listBox.SelectedItem).Execute();
This pattern gives clean separation between all your operations and makes future extensions easy and clean. For example you could add a property CanExecute
to all operations if you have to check if a operation is currently available. Or if you have to support localization it is easy to add logic for presenting the name of the operation in the current UI language.
Another scenario that is easily supported is if you have some code common to all operations for example logging, security checks, performance measuring and things like that.
internal abstract class Operation
{
protected readonly MyForm form = null;
protected Operation(MyForm form)
{
this.form = form;
}
public abstract String DisplayName { get; }
protected abstract void ExecuteCore();
internal void Execute()
{
Logger.Log("Executing operation " + this.DisplayName);
try
{
this.ExecuteCore();
Logger.Log("Executing operation " + this.DisplayName + " succeeded.");
}
catch (Exception exception)
{
Logger.Log("Executing operation " + this.DisplayName + " failed.", exception);
throw;
}
}
}
Note that you now have to override ExecuteCore()
instead of Execute()
.
One final thought - using an interface IOperation
instead or in combination with the abstract base class may be helpful, too. This removes the need that all operation inherit from the same base class because this might sometimes be inconvenient. But I omitted this to not overengineere this even more.
回答3:
You can't* enumerate case
of switch
with normal code.
What you can do instead is to replace switch
with map of "action name" to "action handler" and than you'll be able to reuse this map for list of action names listbox. See Tilak's answer for sample.
*) If you are really inquisitive you can enumerate choices of switch
. C# code is transformed to IL and IL can be read with code. So you can get IL for a method, write (or get existing - Parser for C#) parser for IL and find implementation of switch
inside the method, pick all cases. You can even go straight to C# source at build time - but it is even more involved than IL parsing.
回答4:
Yes there is a way to do this by making a dictionary of lambdas.
void Main()
{
// set up your dictionary
Dictionary<string,Action> myList = new Dictionary<string,Action> {
{ "one", () => { Console.WriteLine("One function"); } },
{ "two", () => { Console.WriteLine("Two function"); }},
{ "three", () => { Console.WriteLine("Three function"); }}
};
// do a "switch" (that is invoke a function that corresponds to a name)
myList["one"]();
// loop the list of keys (that is get a list of all the names)
foreach (string key in myList.Keys)
Console.WriteLine(key);
}
the output of this program:
One function
one
two
three
Also note -- you can add to this "switch" dynamically like this (which is cool and something you can't do with a classical switch statement.)
myList.Add("four",() => { Console.WriteLine("Four function is dynamic"); });
回答5:
It sounds to me like the number of cases in your switch are going to change a lot. If this is true, then you might want to consider using a mechanism other than a switch statement. Perhaps you want to do something like Alexi Levenkov suggests, and then iterate a list of the stored Action Names and execute the associated handler. This way you will avoid having to add the action name to the action map and then add it to the switch.
来源:https://stackoverflow.com/questions/14244455/how-to-store-a-list-of-cases-from-switch-statement