most effective way to determine a control's type in C#/WPF
I have a function that takes a control as parameter, and depending on the control's type (e.g.: TextBox, ComboBox, RadioButton etc...), it executes type-specific code:
internal static void DoSomething(Control control)
{
if (control is Button)
{
// code for button
}
else if (control is Check开发者_开发技巧Box)
{
// code for CheckBox
}
else if (control is TextBox)
{
// code for TextBox
}
// etc.....
}
I am wondering if this is the best way to do.
I know of some other ways to do the same thing (e.g.: looking for the control's type using GetType()
, switching on the string representation of this type), and Microsoft's code analysis tool tells me to use 'as' instead of 'is' like this (because it is better performance-wise):
internal static void DoSomething(Control control)
{
Button button = control as Button
if (button != null)
{
// code for button
}
else
{
CheckBox checkBox = control as CheckBox;
if (checkBox != null)
{
// code for CheckBox
}
else
{
TextBox textBox = control as TextBox;
if (textBox != null)
{
// code for TextBox
}
// etc.....
}
}
}
but I find this last solution rather wordy and not very practical to read. I'd like to be able to switch on the control's type directly, but am unable do to so without resorting to use the string representation (which I don't like at all) as a switch's case statement cannot contain a variable.
So what really is the best way to do performance-wise? and what is, in you opinion, the best way to do? (not necessarily performance-wise, but "code-readability-wise" for instance)
Edit: as a lot is going on on the subject of "why do I use one common function and not many type-specific methods", here is some more info:
I get a control variable from an other part of the app I'm working on (type = Control) and I have do "do something" with this variable, depending on its type.
so basically, I have the choice between 2 options: either I use one common function and check the control's type in the function's body so that I execute the right portion of the code at some point (options I have chosen for now, but this could change), or I check for the control's type BEFORE calling a type-specific method.
either way, I have to switch on the control's type at some point, and THIS is the subject of my question (regardless of what I do with it, if I may say so).
I would use Dictionary
for it and (also separate methods for each handler):
private static readonly Dictionary<Type, Action<Control>> _handlers
= new Dictionary<Type, Action<Control>>();
// Handle.. methods
private static void HandleButton(Button button) { ... }
private static void HandleListbox(Listbox listbox) { ... }
private static void RegisterHandler<T>(Action<T> handler)
where T: Control
{
_handlers.Add(typeof(T), o => handler((T)o));
}
// invoke this method in static constructor
private static void InitializeHandlers()
{
RegisterHandler<Button>(HandleButton);
RegisterHandler<Listbox>(HandleListbox);
}
// finally usage:
internal static void DoSomething(Control control)
{
var handler = _handlers[control.GetType()];
handler(control);
}
The benefit for this approach is some maintainability improvement:
1. You will know that you haven't registered several handlers for same parameter type (dictionary will throw an exception)
2. You will have all your handler registrations separately which will allow you easily finding out which method handles particular parameter type.
3. Since all handler locating logic is not repeated at all it is pretty easy to modify it in order to handle inhereting types for example (my code doesn't do this but your code did)
Well, you don't need to nest on the second one use else if
.
Second why are you putting all of this into one method? It would be better since at the time that you are calling this you should know what the type of the control that it is calling this method is and from there just Do the DoSomething for that control type instead of all of this conditional checking.
I think you're fine using the "is" operator here. It's more readable, and you don't really have any useful alternate path in the case that the control isn't what you were looking for anyway. I don't believe the timing differences will be that critical in this case.
You could swap out the "else if"s for a series of plain "if"s by returning from each individual if block, but that's a personal style choice.
It would be better to refactor the generic (control-agnostic) functionality into a separate function, and have the control-specific functionality in control-specific functions.
You can then call the generic function from the control-specific function where appropriate.
This is the kind of solution I would opt for:
internal class MyClass
{
private const string ButtonTypeAsString = "Button";
private const string CheckBoxTypeAsString = "CheckBox";
private const string TextBoxTypeAsString = "TextBox";
private static string GetTypeAsString(Control control)
{
string result = String.empty;
if (result.Length == 0 && (control as Button) != null)
{
result = MyClass.ButtonTypeAsString;
}
if (result.Length == 0 && (control as CheckBox) != null)
{
result = MyClass.CheckBoxTypeAsString;
}
if (result.Length == 0 && (control as TextBox) != null)
{
result = MyClass.TextBoxTypeAsString;
}
if (result.Length == 0)
{
throw new InvalidOperationException("Control type is not handled by this method.");
}
return result;
}
internal static void DoSomething(Control control)
{
string controlTypeAsString = MyClass.GetTypeAsString(control);
switch (controlTypeAsString)
{
case MyClass.ButtonTypeAsString:
// Button stuff
break;
case MyClass.CheckBoxTypeAsString:
// Checkbox stuff
break;
case MyClass.TextBoxTypeAsString:
// TextBox stuff
break;
default:
throw new InvalidOperationException("Unexpected Control type");
}
}
}
... but I'm sure some would find this overkill. Personally, I like the readability of the switch
statement and try to use it whenever possible. Also, avoiding switching on "Magic Strings." Use const strings
when possible.
If you don't mind me asking, what is it exactly you're trying to do? There may be a better solution that doesn't involve having to infer a Control's type.
精彩评论