switch case and efficiency

  • Thread starter Thread starter Martijn Mulder
  • Start date Start date
M

Martijn Mulder

I have well over a thousend cases after a switch.

1. Is there a practical limit to the number of cases?
2. Can I optimize (and uglify) my code so that switch-case runs faster?
3. How is switch implemented (binary search, linear search...)?

Thank you
 
Martijn Mulder said:
I have well over a thousend cases after a switch.

1. Is there a practical limit to the number of cases?
2. Can I optimize (and uglify) my code so that switch-case runs faster?
3. How is switch implemented (binary search, linear search...)?

Thank you


According to MSDN, there is no limit to the number of cases. This is
stated on this page: http://msdn.microsoft.com/en-us/library/06tc147t.aspx.
Since the first case statement is matched, I think it is just a linear
search as the logic matches the first found case.

Not knowing what you are doing with each of the thousands of cases, it would
be hard to recommend what you should do to optimize things. If you are
using a switch statement to find the house address of a list of phone
numbers, then obviously a better way can be recommended.
 
Martijn said:
I have well over a thousend cases after a switch.
Then you may have a design issue, and a possible maintenance issue. I do
hope that file is machine generated. Even so, see if there's a more compact
way. Are all the case statements unique, with no opportunity for generalization?
1. Is there a practical limit to the number of cases?

I honestly don't know. When your compiler times out, eats up too much memory
or crashes with an internal error, you've probably hit it. This is the most
likely case. It would also be possible for the CLR to hit the limit as it's
loading or JIT-compiling your code, but this seems less likely.

There are no relevant theoretical upper limits. If we assume that the switch
is always implemented with a jump table (i.e. there is no opportunity for
optimizing it further by rewriting the case blocks) then it can't have more
than 4 294 967 295 cases, but this will hardly be the limiting factor in any
case (for starters, the table alone would take up 16 GiB). And even then
there's no rule that it has to be implemented like this.

This is one of those "if you have to ask, it's probably time to rethink your
solution" kind of limits.
2. Can I optimize (and uglify) my code so that switch-case runs faster?

There is no *general* way to optimize a switch, it's already the purest
expression of "if this, then that". Depending on the specifics (what you're
doing in every case block, what values you're using for both switch and
cases and what the compiler/runtime are doing) it may be possible to
optimize it. For example, you could replace it with something else
altogether, like a hashtable and a general function.
3. How is switch implemented (binary search, linear search...)?
It depends on the compiler, the CLR and the specific statement.

The compiler may choose to emit a series of compares and jumps (likely if
the switches are wildly disparate), or it may emit a switch opcode (if the
switches are consecutive) or a combination of these. That's just what
Microsoft's current C# compiler does, mind you -- certainly nothing
prohibits the compiler from, say, generating a minimal perfect hash function
and using that to index a jump table. (Which, incidentally, is another
possible way of optimizing your switch by replacing it with something else.)

These opcodes may in turn be compiled by the CLR in whatever way it chooses.
I'm not aware of any guarantee on the efficiency of the switch statement,
and the CIL specification certainly doesn't mandate one. The switch opcode
(which requires consecutive, zero-based values) is almost certainly
implemented as an indexed jump, as there's no general way to do it better.
The CLR may or may not recognize a series of compares-and-jumps as a
high-level switch statement and optimize accordingly.

In short: if you're that interested in efficiency, profile.
 
I have well over a thousend cases after a switch.
According to MSDN, there is no limit to the number of cases. This is
stated on this page:
http://msdn.microsoft.com/en-us/library/06tc147t.aspx. Since the first
case statement is matched, I think it is just a linear search as the logic
matches the first found case.

Not knowing what you are doing with each of the thousands of cases, it
would be hard to recommend what you should do to optimize things. If you
are using a switch statement to find the house address of a list of phone
numbers, then obviously a better way can be recommended.


In fact, the number is only a few hundreds, but rising. The great number
stems from a recent attempt to simplify key handling code with this
technique:

override protected void OnKeyDown(System.Windows.Forms.KeyEventArgs a)
{
base.OnKeyDown(a);
switch(ModifierKeys.ToString()+' '+a.KeyCode)
{
case"Shift, Control F":
System.Console.WriteLine("You pressed the Shift, Control and F key
similtaniously" );
break;
}
}

It's neat, but it adds up
 
Martijn said:
In fact, the number is only a few hundreds, but rising. The great number
stems from a recent attempt to simplify key handling code with this
technique:

override protected void OnKeyDown(System.Windows.Forms.KeyEventArgs a)
{
base.OnKeyDown(a);
switch(ModifierKeys.ToString()+' '+a.KeyCode)
{
case"Shift, Control F":
System.Console.WriteLine("You pressed the Shift, Control and F key
similtaniously" );
break;
}
}
Well, you might have mentioned that you were switching on strings, as it's
quite different from switching on integers.

The compiler will emit a lazily initialized hashtable containing all the
strings you're switching on, mapping them to integers. These, in turn, will
be used in a regular switch opcode.

Pretend you're building a hashtable yourself containing all the strings
you're switching on and you'll have a better idea of the limits. For
starters, if you have a great many cases, you'll be eating up a great deal
of memory. Switching is also none too efficient, as computing a hash over a
string is far less efficient than looking up a single number.

In any case, you probably don't need a switch statement at all, and I fail
to see how this "simplifies" key handling code, other than by saving you
conscious thought about what the cases have in common. The statement you
give as an example could be easily rewritten to a single series of print
statements. Even if your actual code is more complex, it's still very likely
that you could reduce it to a limited number of numerical switch statements
(for starters, every key plus modifiers easily fits in an int).
 
Jeroen Mostert said:
Well, you might have mentioned that you were switching on strings, as it's
quite different from switching on integers.

The compiler will emit a lazily initialized hashtable containing all the
strings you're switching on, mapping them to integers. These, in turn,
will be used in a regular switch opcode.

Pretend you're building a hashtable yourself containing all the strings
you're switching on and you'll have a better idea of the limits. For
starters, if you have a great many cases, you'll be eating up a great deal
of memory. Switching is also none too efficient, as computing a hash over
a string is far less efficient than looking up a single number.

In any case, you probably don't need a switch statement at all, and I fail
to see how this "simplifies" key handling code, other than by saving you
conscious thought about what the cases have in common. The statement you
give as an example could be easily rewritten to a single series of print
statements. Even if your actual code is more complex, it's still very
likely that you could reduce it to a limited number of numerical switch
statements (for starters, every key plus modifiers easily fits in an int).


IIUC the compiler will generate an efficient int-based switch that I cannot
beat, while I keep the readability, the flexibility and the extensibility on
my side? I am more than satisfied with that :)

Thank you
 
Martijn said:
IIUC the compiler will generate an efficient int-based switch that I cannot
beat, while I keep the readability, the flexibility and the extensibility on
my side? I am more than satisfied with that :)
I have no idea how you derive this conclusion from my statements. Are you
saying you're *not* using strings?

If you're not actually interested in a full answer to the question, just in
hearing that what you're doing works and will probably not be too
inefficient: what you're doing works and will probably not be too
inefficient. Most optimizations aren't necessary.
 
Jeroen said:
Then you may have a design issue, and a possible maintenance issue. I do
hope that file is machine generated. Even so, see if there's a more
compact way. Are all the case statements unique, with no opportunity for
generalization?

Just as some define procedural programming as goto free
programming, then other define object oriented programming
as switch free programming.

There are also plenty of coding conventions that
limit method length to 50 or 100 lines.

I don't agree with those rules being the ultimate truth.
But non the less a method violating several common
coding rules is a good indication that something
is wrong.

Conclusion: I had to use 9 lines to say that I agree
with Jeroen.

Arne
 
I have no idea how you derive this conclusion from my statements. Are you
saying you're *not* using strings?

If you're not actually interested in a full answer to the question, just
in hearing that what you're doing works and will probably not be too
inefficient: what you're doing works and will probably not be too
inefficient. Most optimizations aren't necessary.


I am working on the interface of an application and I want to define
shortcut keys to some of the menu - or toolbar items. Some are
straightforward (Ctrl-P for printing, F1 for help), but on many I still have
to decide.

To make it easy to switch from one option to the other I deviced an
elaborate switch-case that holds almost all key combinations. They are
almost all empty, but I can find them in one place and they are organized
alphabetically. When the final decisions are made, still 80 percent of the
key combinations will be unused and can be deleted.

And that's were errors creep in.

So I am trying to decide if it is worth leaving all the empty cases intact,
sacrificing some efficiency but lowering the chance of deleting a keyboard
shortcut by accident.

This is the context of my question. The method OnKeyDown is now 1763 lines
long. I cherish it as 'the long method' :)
 
That's way too long. Methods that are this long are hard to maintain.
Also, how do you handle the user keeping a key pressed? The OnKeyDown
method is called everytime the keyboard triggers a key repeat. OnKeyUp is
only called when the key is released.


The good news is that the compiler throws out all empty cases without a
trace. So I have a very broad range of cases to choose from, and I only pay
for what I use. The compiler works really hard for me :)

Also, what is hard to maintain about the following code?

//method OnKeyDown
override protected void OnKeyDown(System.Windows.Forms.KeyEventArgs a)
{
base.OnKeyDown(a);
switch(ModifierKeys.ToString()+' '+a.KeyCode)
{
case"Control A":
break;
case"Control Add":
break;
case"Control B":
break;
case"Control C":
break;
case"Control Clear":
break;
case"Control D":
break;
case"Control Delete":
break;
case"Control Divide":
break;
case"Control Down":
break;
case"Control E":
break;
case"Control End":
break;
//...
//etc
case"Shift, Control X":
break;
case"Shift, Control Y":
break;
case"Shift, Control Z":
break;
}
}
 
In fact, the number is only a few hundreds, but rising. The great number
stems from a recent attempt to simplify key handling code with this
technique:

override protected void OnKeyDown(System.Windows.Forms.KeyEventArgs a)
{
base.OnKeyDown(a);
switch(ModifierKeys.ToString()+' '+a.KeyCode)
{
case"Shift, Control F":
System.Console.WriteLine("You pressed the Shift, Control and F key
similtaniously" );
break;
}
}

It's neat, but it adds up

I highly recommend you look into something called the "command pattern." You
encapsulate the functionality of every one of your keystrokes into classes
(each of which implement an interface) and then you run the command by
calling the Execute() method. After all, every one of your shortcuts
represents a discrete action, like Print, Save, Delete, right?

Then what you could do is create a dictionary to hold the keystroke and the
command. It would look like this:

Dictionary<System.Windows.Forms.Keys, Command> _cmds;

You see, all the information about the the keys that are pressed is stored
in a single Keys enumeration in the KeyEventArgs object you get. It's called
KeyData. Every other property (not counting the Booleans) of KeyEventArgs is
just a helper property that pulls out specific data from KeyData. Therefore
any given combination of keystrokes equates to a unique KeyData value.

So now all you have is some pre-work to do in creating the commands and
adding them to the dictionary*, and your switch statement disappears
completely. Your code will look like this (and yes, you should use KeyUp
unless you want it to repeat):

override protected void OnKeyUp(System.Windows.Forms.KeyEventArgs a)
{
// Are you sure you want to do this before you've decided if
// you want to handle the key?
base.OnKeyUp(a);

if (_cmds.Contains[a.KeyData])
{
_cmds[a.KeyData].Execute();
}
else
{
// You wouldn't really do this in a production app.
// (Unless you have a death wish.)
MessageBox.Show("Unknown keystroke detected.");
}
}

You mentioned "readability, flexibility and extensibility" as being
important. Your setup code will look like this:

_cmds.Add(Keys.Control | Keys.P, new PrintCommand());
_cmds.Add(Keys.Delete, new DeleteCommand());
_cmds.Add(Keys.Control | Keys.Shift | Keys.N, new SetNormalFormatCommand());

Is that readable enough for you?

As a bonus, what's really really really cool about the command pattern is
that it makes it incredibly easy to add multiple undo/redo to your app.


*I'm oversimplifying this. You have the entire "implementing the command
pattern" thing to do.
 
Also, what is hard to maintain about the following code?

Maybe the fact that you've tied yourself to the way the .NET Framework
handles ModifierKeys.ToString()? What if, for some crazy reason, MS decides
to output "Control, Shift" instead of "Shift, Control" in a future version
of the framework? It's a really bad idea to rely on strings.
 
I highly recommend you look into something called the "command pattern".
As a bonus, what's really really really cool about the command pattern is
that it makes it incredibly easy to add multiple undo/redo to your app.

Problem is that not all actions share the same interface. Most are simple
emulations of MenuItem commands:

void Action(){}

but the ArrowKeys mimic mouse movements:

void Translate(int,int){}

and PageUp and PageDown do scaling:

void Scale(float){}

so then I end up with several Dictionaries. I do use generic dictionaries in
the menu-structure, but in a rather unusual way that I shall elaborate on
later (not switching on Strings, nono). For now, thanks for your advice. It
gives me a lot to think about.
 
Problem is that not all actions share the same interface. Most are simple
emulations of MenuItem commands:

void Action(){}

but the ArrowKeys mimic mouse movements:

void Translate(int,int){}

and PageUp and PageDown do scaling:

void Scale(float){}

so then I end up with several Dictionaries. I do use generic dictionaries
in the menu-structure, but in a rather unusual way that I shall elaborate
on later (not switching on Strings, nono). For now, thanks for your
advice. It gives me a lot to think about.

Why would you end up with several dictionaries? The parameters to the
various methods you mention are not passed in the OnKeyUp() method, so you
must hold them elsewhere. Why not simply hold them in the commands you
create? You wouldn't call

Command.Execute(int, int)

or

Command.Execute(float)

Instead, you'd make properties specific to those commands and set them
first, then call the standard, parameterless Execute() in OnKeyUp() like any
other command. Example:

ScalingCommand pageUpCommand = new ScalingCommand();
pageUpCommand.ScalingFactor = 1.1f;
_cmds.Add(Keys.PageUp, pageUpCommand);

ScalingCommand pageDownCommand = new ScalingCommand();
pageDownCommand.ScalingFactor = 0.9f;
_cmds.Add(Keys.PageDown, pageDownCommand);

See where I'm going with this? (You could of course replace a single scaling
factor with an array of factors indicating "zoom stops." The possibilities
are endless.)

By the way...page up/down = scale view? Really? Why not Ctrl+= and Ctrl+-?
("=" since it's below the "+" key, at least on my keyboard. Or the num pad
+/- keys.)
 
Michael D. Ober said:
Jeff Johnson said:
In fact, the number is only a few hundreds, but rising. The great number
stems from a recent attempt to simplify key handling code with this
technique:

override protected void OnKeyDown(System.Windows.Forms.KeyEventArgs a)
{
base.OnKeyDown(a);
switch(ModifierKeys.ToString()+' '+a.KeyCode)
{
case"Shift, Control F":
System.Console.WriteLine("You pressed the Shift, Control and F key
similtaniously" );
break;
}
}

It's neat, but it adds up

I highly recommend you look into something called the "command pattern."
You encapsulate the functionality of every one of your keystrokes into
classes (each of which implement an interface) and then you run the
command by calling the Execute() method. After all, every one of your
shortcuts represents a discrete action, like Print, Save, Delete, right?

Then what you could do is create a dictionary to hold the keystroke and
the command. It would look like this:

Dictionary<System.Windows.Forms.Keys, Command> _cmds;

You see, all the information about the the keys that are pressed is
stored in a single Keys enumeration in the KeyEventArgs object you get.
It's called KeyData. Every other property (not counting the Booleans) of
KeyEventArgs is just a helper property that pulls out specific data from
KeyData. Therefore any given combination of keystrokes equates to a
unique KeyData value.

So now all you have is some pre-work to do in creating the commands and
adding them to the dictionary*, and your switch statement disappears
completely. Your code will look like this (and yes, you should use KeyUp
unless you want it to repeat):

override protected void OnKeyUp(System.Windows.Forms.KeyEventArgs a)
{
// Are you sure you want to do this before you've decided if
// you want to handle the key?
base.OnKeyUp(a);

if (_cmds.Contains[a.KeyData])
{
_cmds[a.KeyData].Execute();
}
else
{
// You wouldn't really do this in a production app.
// (Unless you have a death wish.)
MessageBox.Show("Unknown keystroke detected.");
}
}

You mentioned "readability, flexibility and extensibility" as being
important. Your setup code will look like this:

_cmds.Add(Keys.Control | Keys.P, new PrintCommand());
_cmds.Add(Keys.Delete, new DeleteCommand());
_cmds.Add(Keys.Control | Keys.Shift | Keys.N, new
SetNormalFormatCommand());

Is that readable enough for you?

As a bonus, what's really really really cool about the command pattern is
that it makes it incredibly easy to add multiple undo/redo to your app.


*I'm oversimplifying this. You have the entire "implementing the command
pattern" thing to do.
Jeff,

Is there some place I can go to get more information on this? I need to
port some VB6 code that uses something similar.

Thanks,
Mike Ober.


Go after the book 'Design Patterns, Elements of Reusable Object-Oriented
Software', Gamma, Helm, Johnson, Vlissides, Addison-Wesley. If There Is Only
One Computer Book That You etc :)
 
Is there some place I can go to get more information on this? I need to
port some VB6 code that uses something similar.

Argh. I'm trying to find the original article I read which opened my eyes to
this. I'm pretty sure it was something I found while looking for ideas on
how to implement multiple undo/redo, and I'm virtually positive it was on
CodeProject, but can I find it now? Nooooooooooooo. And unfortunately I
didn't download any source code (if there even was any) so I can't backtrack
via the readme.\

Do some searching, read up a bit, and then start a thread with questions.
I'll help however I can. I got it to work for me, although I'm sure my
solution wasn't perfect from an OOP perspective.
 
Do some searching, read up a bit, and then start a thread with questions.
I'll help however I can. I got it to work for me, although I'm sure my
solution wasn't perfect from an OOP perspective.

Hi Jeff,

I wrote a pretty short Forms-application with a mini-menu (File -> New,
Print, Exit). Can you help me (and others) to turn this into a program that
implements the Command Pattern? I'd love to see it

/*
Command.cs
*/


//class Form
class Form:System.Windows.Forms.Form
{


//data menustrip
System.Windows.Forms.MenuStrip menustrip=new
System.Windows.Forms.MenuStrip();


//data menuitem1, menuitem2, menuitem3, menuitem4
System.Windows.Forms.ToolStripMenuItem menuitem1=new
System.Windows.Forms.ToolStripMenuItem();
System.Windows.Forms.ToolStripMenuItem menuitem2=new
System.Windows.Forms.ToolStripMenuItem();
System.Windows.Forms.ToolStripMenuItem menuitem3=new
System.Windows.Forms.ToolStripMenuItem();
System.Windows.Forms.ToolStripMenuItem menuitem4=new
System.Windows.Forms.ToolStripMenuItem();


//constructor
Form()
{
Text="The Command Pattern";
menuitem1.Text="File";
menuitem2.Text="New";
menuitem3.Text="Print";
menuitem4.Text="Exit";
menuitem2.Click+=OnClick;
menuitem3.Click+=OnClick;
menuitem4.Click+=OnClick;
menuitem1.DropDown.Items.AddRange
(
new System.Windows.Forms.ToolStripMenuItem[]
{
menuitem2,
menuitem3,
menuitem4,
}
);
menustrip.Items.Add(menuitem1);
Controls.Add(menustrip);
Show();
}


//method OnClick
void OnClick(object a,System.EventArgs b)
{
switch(((System.Windows.Forms.ToolStripMenuItem)a).Text)
{
case"New":
New();
break;
case"Print":
Print();
break;
case"Exit":
Exit();
break;
}
}


//method Exit
void Exit()
{
System.Windows.Forms.Application.Exit();
}


//method New
void New()
{
System.Windows.Forms.MessageBox.Show("New");
}


//method Print
void Print()
{
System.Windows.Forms.MessageBox.Show("Print");
}


//method Main
static void Main()
{
System.Windows.Forms.Application.Run(new Form());
}
}
 
Hi Jeff,

I wrote a pretty short Forms-application with a mini-menu (File -> New,
Print, Exit). Can you help me (and others) to turn this into a program
that implements the Command Pattern? I'd love to see it

The following is an incredibly simple recreation of the program you posted,
along with keyboard shortcut support. By itself, it just makes the code
overly complicated. The concept, however, becomes far more powerful as the
application becomes more complex.

Note that I created two dictionaries: one to hold the commands (with a
made-up ID) and one to hold a cross-reference (an index) for keyboard
shortcuts. It's doubtful that every single one of your menu items will have
a keyboard shortcut, so you'll end up with more items in the command list
than you will in the index. (In this example, 3 commands, 2 shortcuts).

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace CommandPattern
{
//class Form
class MainForm : System.Windows.Forms.Form
{
//data menustrip
System.Windows.Forms.MenuStrip menustrip = new
System.Windows.Forms.MenuStrip();


//data menuitem1, menuitem2, menuitem3, menuitem4
System.Windows.Forms.ToolStripMenuItem menuitem1 = new
System.Windows.Forms.ToolStripMenuItem();
System.Windows.Forms.ToolStripMenuItem menuitem2 = new
System.Windows.Forms.ToolStripMenuItem();
System.Windows.Forms.ToolStripMenuItem menuitem3 = new
System.Windows.Forms.ToolStripMenuItem();
System.Windows.Forms.ToolStripMenuItem menuitem4 = new
System.Windows.Forms.ToolStripMenuItem();

private Dictionary<int, ICommand> _commands;
private Dictionary<Keys, int> _keysToCommandsXref;

private const int NEW_COMMAND_ID = 1;
private const int PRINT_COMMAND_ID = 2;
private const int EXIT_COMMAND_ID = 3;

//constructor
public MainForm()
{
// Set up the commands and connect them to keyboard shortcuts, if
any
_commands = new Dictionary<int, ICommand>();
_keysToCommandsXref = new Dictionary<Keys, int>();

// The New command plus its keyboard shortcut: Ctrl+N
_commands.Add(NEW_COMMAND_ID, new NewCommand());
_keysToCommandsXref.Add(Keys.Control | Keys.N, NEW_COMMAND_ID);

// The Print command plus its keyboard shortcut: Ctrl+P
_commands.Add(PRINT_COMMAND_ID, new PrintCommand());
_keysToCommandsXref.Add(Keys.Control | Keys.P, PRINT_COMMAND_ID);

// The Exit command. It needs no keyboard shortcut because of the
// system-wide Alt+F4.
_commands.Add(EXIT_COMMAND_ID, new ExitApplicationCommand());

Text = "The Command Pattern";
menuitem1.Text = "File";
menuitem2.Text = "New";
menuitem2.Tag = NEW_COMMAND_ID;
menuitem3.Text = "Print";
menuitem3.Tag = PRINT_COMMAND_ID;
menuitem4.Text = "Exit";
menuitem4.Tag = EXIT_COMMAND_ID;
menuitem2.Click += OnMenuItemClick;
menuitem3.Click += OnMenuItemClick;
menuitem4.Click += OnMenuItemClick;
menuitem1.DropDown.Items.AddRange(
new System.Windows.Forms.ToolStripMenuItem[]
{
menuitem2,
menuitem3,
menuitem4,
});
menustrip.Items.Add(menuitem1);
Controls.Add(menustrip);
Show();
}


//method OnMenuItemClick
private void OnMenuItemClick(object sender, System.EventArgs e)
{
int commandId = (int)((ToolStripMenuItem)sender).Tag;

if (_commands.ContainsKey(commandId))
{
_commands[commandId].Execute();
}
else
{
// Oops!
}
}

protected override void OnKeyUp(KeyEventArgs e)
{
if (_keysToCommandsXref.ContainsKey(e.KeyData))
{
_commands[_keysToCommandsXref[e.KeyData]].Execute();
}
else
{
base.OnKeyUp(e);
}
}

//method Main
static void Main()
{
System.Windows.Forms.Application.Run(new MainForm());
}
}

// There is actually an existing interface that is exactly like this:
// System.Windows.Forms.ICommandExecutor
public interface ICommand
{
void Execute();
}

internal class NewCommand : ICommand
{
public void Execute()
{
MessageBox.Show("New");
// In a real implementation, you'd have to provide a
// means for the command to talk to the main form (or
// perhaps some sort of shared class) so that it can
// instruct the target to create a new document
}
}

internal class PrintCommand : ICommand
{
public void Execute()
{
MessageBox.Show("Print");
}
}

internal class ExitApplicationCommand : ICommand
{
public void Execute()
{
Application.Exit();
}
}
}
 
In the naive case where the method to Execute has signature void Execute( ),
as in most cases, you can spare a few internal classes by using delegates:


//using...
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;


//namespace CommandPattern
namespace CommandPattern
{

//class MainForm
class MainForm : Form
{

//enum ID
enum ID
{
NEW_COMMAND,
PRINT_COMMAND,
EXIT_COMMAND,
};

//delegate Execute
delegate void Execute();

//data _delegates
Dictionary<ID, Execute> _delegates=new
Dictionary<ID, Execute>();

//data _keysToCommandsXref
Dictionary<Keys, ID> _keysToCommandsXref=new
Dictionary<Keys, ID>();

//data menustrip
MenuStrip menustrip = new MenuStrip();

//data menuitem1, menuitem2, menuitem3, menuitem4
ToolStripMenuItem menuitem1 = new ToolStripMenuItem();
ToolStripMenuItem menuitem2 = new ToolStripMenuItem();
ToolStripMenuItem menuitem3 = new ToolStripMenuItem();
ToolStripMenuItem menuitem4 = new ToolStripMenuItem();

//constructor
public MainForm()
{

// Set caption
Text = "The Command Pattern";

// Fill _delegates with New, Print and ExitApplication
_delegates.Add(ID.NEW_COMMAND, New);
_delegates.Add(ID.PRINT_COMMAND, Print);
_delegates.Add(ID.EXIT_COMMAND, ExitApplication);

// Fill _keysToCommandsXref with New and Print
_keysToCommandsXref.Add
(
Keys.Control|Keys.N,
ID.NEW_COMMAND
);
_keysToCommandsXref.Add
(
Keys.Control|Keys.P,
ID.PRINT_COMMAND
);

// Setup menu
Controls.Add(menustrip);
menustrip.Items.Add(menuitem1);

menuitem1.Text = "File";
menuitem1.DropDown.Items.Add(menuitem2);
menuitem1.DropDown.Items.Add(menuitem3);
menuitem1.DropDown.Items.Add(menuitem4);

menuitem2.Click += OnMenuItemClick;
menuitem2.Tag = ID.NEW_COMMAND;
menuitem2.Text = "New";

menuitem3.Click += OnMenuItemClick;
menuitem3.Tag = ID.PRINT_COMMAND;
menuitem3.Text = "Print";

menuitem4.Click += OnMenuItemClick;
menuitem4.Tag = ID.EXIT_COMMAND;
menuitem4.Text = "Exit";
}

//methods ExitApplication, New, Print
void ExitApplication() {Application.Exit();}
void New() {MessageBox.Show("New");}
void Print() {MessageBox.Show("Print");}

//method OnMenuItemClick
private void OnMenuItemClick(object sender,EventArgs e)
{
try
{
_delegates[(ID)((ToolStripMenuItem)sender).Tag]();
}
catch
{
// Oops!
}
}

//method OnKeyUp
protected override void OnKeyUp(KeyEventArgs e)
{
try
{
_delegates[_keysToCommandsXref[e.KeyData]]();
}
catch
{
base.OnKeyUp(e);
}
}

//method Main
static void Main()
{
Application.Run(new MainForm());
}
}
}
 
In the naive case where the method to Execute has signature void
Execute( ), as in most cases, you can spare a few internal classes by
using delegates:

Yes, you can. Any time you would actually want to implement the command
pattern, however, you'll almost assuredly have more complex command classes
than the ones I demonstrated, especially if you're going to support undo, so
using a delegate, if even possible, would only serve to fracture your code.
The best bet is to put all the functionality into the command class.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top