33

I have an event handler for a Textbox and a RichTextBox. The code is identical, but

In handler #1 I do:

RichTextBox tb = (RichTextBox)sender

In handler #2 accordingly:

TextBox tb = (TextBox)sender

Doing so I can fully manipulate the sending control. How can I cast the sending object to Textbox or RichTextbox according to its type using

sender.GetType().Name

and then create the control at runtime and work with it? That way I only need one event handler function: less code, less errors, easier to maintain and DRY :-)

jordanz
  • 367
  • 4
  • 12
tfl
  • 1,011
  • 2
  • 12
  • 21
  • Can you give an example of a polymorphic method or property which is shared between two such types? and yet is not exposed by a common interface? – AnthonyWJones Mar 11 '09 at 11:23

10 Answers10

38

You never have to cast. I used to think the same way when I started, this 'pattern' is incorrect, and not really logical.

Your best bet is to use something like:

if (sender is TextBox)
{
  TextBox tb = (TextBox)sender;
}
else if (sender is RichTextBox)
{
  RichTextBox rtb = (RichTextBox)sender;
}
else
{
  // etc
}
leppie
  • 115,091
  • 17
  • 196
  • 297
  • 2
    You're actually casting once you've determined the object's type, so you *do* have to cast, but this is definitely the right way to do it as it doesn't rely on (slow) reflection. – idmadj Jul 31 '14 at 15:36
9

I know this is a very old post but in Framework 4 you can cast the sender as a Control:

Control cntrl = (Control)sender;
cntrl.Text = "This is a " + sender.GetType().ToString();

Note you are only able to reference controls that all of the different controls have in common (ie Text).

Mark Kram
  • 5,672
  • 7
  • 51
  • 70
8

Rather than the type name you could use 'is'.

If you just want to know the type and don't need an object reference:

if (sender is RichTextBox)
{
    // ...
}
else if (sender is TextBox)
{
    // ...
}

However you generally do want the object: C#7 has a nice syntax that allows you to test and get the value inline:

if (sender is RichTextBox richTextBox)
{
    richTextBox.Text = "I am rich";
}
else if (sender is TextBox textBox)
{
    textBox.Text = "I am not rich";
}
stuartd
  • 70,509
  • 14
  • 132
  • 163
  • 1
    This is ok but @Chris suggestion is better. Since your going to do something with the objects immediately it will save you an extra cast. – Peter Lillevold Mar 11 '09 at 11:32
  • That’s a really sweet syntax. Does it have a name? – dakab Sep 17 '21 at 08:07
  • @dakab it’s called “pattern matching” - https://devblogs.microsoft.com/premier-developer/dissecting-the-pattern-matching-in-c-7/ – stuartd Sep 17 '21 at 10:17
4
RichTextBox textbox = sender as RichTextBox;
if (textbox != null)
{
   // do stuff as a rtb
   textbox.Text = "I'm a rtb";
   return;
}

TextBox textbox = sender as TextBox;
if (textbox != null)
{
   // do stuff as a textbox
   textbox.Text = "I'm a textbox";
}
Chris S
  • 64,770
  • 52
  • 221
  • 239
3

Casting can only be done at compile-time and thus you need to know the types that you wish to cast to at compile-time. A runtime Type (as returned by GetType()) can therefore not be used when casting.

If it is polymorphism you are looking for you could access the Name property through reflection. I wouldn't go that way though just to be able to reuse event handlers.

If you want strong typing, a common base class or interface on the two senders is the only way to go.

Peter Lillevold
  • 33,668
  • 7
  • 97
  • 131
3

Depending on what properties you need, you could cast the sender as a TextBoxBase as both the TextBox and RichTextBox both inherit from that sub-class.

Kieron
  • 26,748
  • 16
  • 78
  • 122
1

Generic version of the above code:

public static void CastAndUse<T>(object item, Action<T> action) where T : class
{
    T thing = item as T;

    if (thing != null)
    {
        action(thing);
    }
}

Used as:

CastAndUse(sender, new Action((foo) => foo = bar));

Not perfect, but handy.

Josh
  • 632
  • 7
  • 13
1

You can also use an inline-temporary variable to handle the cast for you.

if (sender is RichTextBox tb)
{
    // ... //
} 
else if (sender is TextBox tb)
{
    // ... //
}
Chris Stillwell
  • 10,266
  • 10
  • 67
  • 77
0

If the code is identical, do you need to care? I wonder if casting to Control wouldn't give you everything you need...

One complex handler is not necessarily better than several simple handlers. Either way, if you have to go this route, "as"/"is" is preferable (it isn't dependent on strings etc):

TextBox tb = sender as TextBox;
if(tb!=null) {/* TextBox specific code */}
...
Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
0

if you dont want to repeat the code then you can cast both the controls, refactor the common actions to a separate method which takes TextBoxBase as an argument. And in your event handlers convert the controls to System.Windows.Forms.TextBoxBase as both controls are derived from the TexbBoxBase and call the method.

Please note If you need specific properties of any of these controls then this refactoring wont work.

gk.
  • 1,252
  • 3
  • 14
  • 23