Switch statement

Hi, all:

[I guess this could be considered a rant, but I'm still curious if anyone shares my opinion on this]

I am still wondering why the switch statement is not more flexible. For example, I feel that a switch statement using objects makes sense. I'll give a more visual example for ease of understanding more than anything else, so picture this: I have three buttons on my form, button1, button2, button3. I set the click event to the same event handler, button_ButtonClick. Inside the button_ButtonClick method I want to check the sender to see which button was clicked, so I do this:


switch(sender)
// or even (sender as Button)
{
case button1:
// do something here
break;
case button2:
// do something here
break;
case button3:
// do something here
break;
}

Of course it doesn't work. I know this can be achieved using the button label (well, debatable when talking about localization), button's name or even a tag, but still...

Oh well, just one of those things ;-)

Thanks for listening,

[1472 byte] By [CalinMac] at [2007-12-24]
# 1
maybe sender.ToString() would work (im guessing, im not sure if it even has such a method)
xRuntime at 2007-10-8 > top of Msdn Tech,Visual C#,Visual C# Language...
# 2
The rant shouldn't be directed towards the switch, but to the sender object. The switch statement needs a typed object to determine path....once an appropriate item is found on sender, I am sure the switch statement will be happy to do your bidding.

I suggest you pose the question to the winform group and see what they come up with.

OmegaMan at 2007-10-8 > top of Msdn Tech,Visual C#,Visual C# Language...
# 3

So you're saying that if instead of buttons my switch would use business objects it would work? OK, example again :-)

I have a base business class, BusinessBase and some inheritors (separate classes instantiated as employee and customer). They all trigger an event when a property has changed. Can I do this when trapping the event?


switch (sender)
// or even (sender as BusinessBase)
{
case employee:
// do something here
break;
case customer:
// do something here
break;
}

I don't think I can. Maybe a typeof(EmployeeClass) and typeof(CustomerClass) would work, but then (to compare apples with apples) if I had 2 instances of the EmployeeClass and needed to know which one sent the event I'm back to square one, no?

Thanks for your reply,

CalinMac at 2007-10-8 > top of Msdn Tech,Visual C#,Visual C# Language...
# 4
I am not doing this post justice ... but I have done a similar thing in the reuse of a call with events. There is a way to divine which button that it originated from ... and I believe the Sender object is used in a way in which it will beswitchable.
OmegaMan at 2007-10-8 > top of Msdn Tech,Visual C#,Visual C# Language...
# 5

Switch does not let you do that, because doing that is very bad design.

Stop fighting OO design, and embrace it.

interface IObjectWhichDoesSomething
{ void DoSomething(); }

class Employee : IObjectWhichDoesSomething
{
// :
void DoSomething();
}

class Customer: IObjectWhichDoesSomething
{
// :
void DoSomething();
}

Then instead of that horribly switch statement, all you need is:

(sender as IObjectWhichDoesSomething).DoSomething();

Don't like that?... Don't worry -- there are a number of ways to handle it. How 'bout this? Why not have different event handlers for the employee object & the customer object?

JamesCurran at 2007-10-8 > top of Msdn Tech,Visual C#,Visual C# Language...
# 6

Umm... I think that's a bit much for me. You mean it's possible to have different event handlers for different objects? Wow, what a concept! Now that's true OO, thank you for illuminating me!

However, I digress. The point that you probably missed (or chose to ignore) was the paragraph after the code example where there are two employee objects (right after compare apples to apples was mentioned). Or the first post, where I was talking about buttons (not necessarily wanting to create a separate class for every single button in my interface). Those were just examples about what I feel is missing -- could be poor by one's standards, but they seemed clear enough to me when I wrote them (although that's also debatable :-)).

Interface or no interface, this is not about embracing OO -- been humbly trying to do that for the past 10 years or so with various languages and I still can't pretend to be qualified enough to tell people with enough certainty or smugness to do that. This is about some functionality that I felt the switch statement should have.

"I disagree with you" would have sufficed since I'm not changing the language (or bashing it in any way, for that matter) with this post. No need to embrace some thing or another, I don't know you well enough, I'm not even sure I like you enough to do that!

Nonetheless, thank you for your reply. Even though I don't entirely agree with your approach (it seems to me that what you wrote, while perfectly valid, has no bearing on my post), the fact that you're allowed to freely reply to my post is extremly important and the fact that you took the time to do that even more so.

CalinMac at 2007-10-8 > top of Msdn Tech,Visual C#,Visual C# Language...
# 7

Wel, first of all, you still haven't said why you want to send the events for two different objects (which have two different reactions to that event) through the same event handler.

But basically you are asking for a way to discriminate between two different objects of the same class. This however, is a problem for the class designer, not the language. Presumably the Employee class has same property which uniquely identifies it, e.g., emp.ID or emp.Name.

JamesCurran at 2007-10-8 > top of Msdn Tech,Visual C#,Visual C# Language...
# 8
I have somewhat of a rant about data types, too. I program in both VB.NET and C#.NET, and I have found that there is no option with the ForEach statement to select a particular control type. (for example, cycle through only all the checkboxes on a form). You have to use a Control object, which is limited because it doesn't allow you to get any of the information specific to the control type you're after (for example the Checked state). However, in VB.NET it is possible to cycle through all of one particular control type and thus have all the functionality you need!
CumQuaT at 2007-10-8 > top of Msdn Tech,Visual C#,Visual C# Language...
# 9
CalinMac wrote:

...there are two employee objects (right after compare apples to apples was mentioned). Or the first post, where I was talking about buttons (not necessarily wanting to create a separate class for every single button in my interface). Those were just examples about what I feel is missing -- could be poor by one's standards, but they seemed clear enough to me when I wrote them (although that's also debatable :-)).

Use the tag object off of the Button class and put in info that will help by creating a seperate struct object to hold identifying data. That way the combined call will work. In this example I only use one button, but it will work for any number of buttons:



public struct theInfo
{
public theInfo( string title, string link )
{
Title = title;
Link = link;
}
public string Title;
public string Link;
}

public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
button1.Tag = new theInfo("Microsoft", "www.microsoft.com");
}

private void button1_Click( object sender, EventArgs e )
{
//if (sender == button1)
// MessageBox.Show("One");

Button currentButton = sender as Button;

if (currentButton != null)
{

theInfo current = (theInfo) currentButton.Tag;

MessageBox.Show(string.Format("Who ({0}) Where ({1})", current.Title, current.Link));
}

}

}

OmegaMan at 2007-10-8 > top of Msdn Tech,Visual C#,Visual C# Language...
# 10

I'm not sure exactly what you are asking for (mainly because I'm not clear on what VB offers), but I'm pretty sure it's the same as C#.

Anyway, in C#, you can do:

foreach(CheckBox cb in form1.Controls)

The catch is (and I'm pretty sure VB is the same way), that for every control which is not a CheckBox, cb will be null, so you have to write it as:

foreach(CheckBox cb in form1.Controls)
{
if (cb != null)
{
// do stuff with cb
}
}

JamesCurran at 2007-10-8 > top of Msdn Tech,Visual C#,Visual C# Language...
# 11

Oh, no, I do realize that. I even mentioned in my first post that "I know this can be achieved using the button label (well, debatable when talking about localization), button's name or even a tag, but still...". I don't have a problem making this work in some way, my classes have enough uniquely identifiable elements to be able to pick and choose one to perform this task. That was not the reason I wrote this, to find out how to do it.

Anyway, this thing (like many others around us :-)) has taken a life of its own, all I wanted to know was if there are other people out there (here?) that feel the same way about the way the switch statement works. Not to complain about it, just to find out -- based on what James says, the way it works may be right, since"Switch does not let you do that, because doing that is very bad design. Stop fighting OO design, and embrace it. "

Good night and good luck :-)

CalinMac at 2007-10-8 > top of Msdn Tech,Visual C#,Visual C# Language...
# 12
That doesn't work. It says unable to cast type button to checkbox. The problem occurs in the original if condition, that's not "legal" code..
xRuntime at 2007-10-8 > top of Msdn Tech,Visual C#,Visual C# Language...
# 13

Oooops... You're right. It doesn't work. But I'm sure I'd done exactly that in the past. OK, nevermind. This does work (I've tested it):

foreach (Control ctrl inthis.Controls)
{
ComboBox cb = ctrl asComboBox;
if (cb != null)
{
// do stuff with cb
}
}

JamesCurran at 2007-10-8 > top of Msdn Tech,Visual C#,Visual C# Language...
# 14

As a new approch to this, this only took a few minutes to put together.

Use it like this:

 1: foreach (ComboBox cbinnew ControlFilter<ComboBox>(this.Controls)) 2: {
 3:  MessageBox.Show(cb.SelectedText); 4: } 

The code for ControlFilter is as follows:

class ControlFilter<T> : IEnumerable<T>where T : Control{
private Control.ControlCollection m_Coll; 
public ControlFilter(Control.ControlCollection coll) {
 m_Coll = coll; }
 #region IEnumerable<Control> Members
 public IEnumerator<T> GetEnumerator()
 {foreach(Control ctrlin m_Coll)
 { T ctrlT = ctrlas T;
if (ctrlT !=null)yieldreturn ctrlT;
 } }
 #endregion
 #region IEnumerable Members
 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
 {returnthis.GetEnumerator();
 } 
#endregion}
JamesCurran at 2007-10-8 > top of Msdn Tech,Visual C#,Visual C# Language...