在C# / WPF中确定控件类型的最有效方法

6

我有一个函数,它接受一个控件作为参数,根据控件的类型(例如:TextBox,ComboBox,RadioButton等),执行特定类型的代码:

internal static void DoSomething(Control control)
{
    if (control is Button)
    {
        // code for button
    }

    else if (control is CheckBox)
    {
        // code for CheckBox
    }

    else if (control is TextBox)
    {
        // code for TextBox
    }

    // etc.....
}

我在想这是否是最好的做法。

我知道有其他一些方法来完成相同的事情(例如:使用 GetType() 查找控件类型,根据该类型的字符串表示进行切换),而微软的代码分析工具告诉我要像这样使用 'as' 而不是 'is'(因为它在性能方面更好):

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.....
        }
    }
}

但我认为这个最后的解决方案过于冗长,不太实用阅读。 我希望能够直接打开控件类型,但是无法这样做而不使用字符串表示(我根本不喜欢),因为switch的情况语句不能包含变量。

那么从性能角度来看,真正的最佳方法是什么?在您的意见中,最佳方法是什么?(不一定是性能方面,例如“代码可读性”)

编辑:关于“为什么我使用一个通用函数而不是许多类型特定的方法”的主题,这里有更多信息:

我从我正在处理的应用程序的其他部分得到一个控制变量(类型=控件),我必须对此变量“做些什么”,这取决于它的类型。

所以基本上,我可以选择两种选项:要么我使用一个公共函数并在函数体中检查控件的类型,以便在某个时刻执行正确的代码部分(我现在选择的选项,但这可能会改变),或者在调用特定于类型的方法之前检查控件的类型。

无论如何,我都必须在某个时候切换控件的类型, 是我的问题的主题(不管我对它做什么,如果我这样说的话)。

5个回答

8
我会使用 Dictionary(字典)来实现,并为每个处理程序定义单独的方法:
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);
}

这种方法的好处在于提高了可维护性:
1. 你会知道你没有为同一参数类型注册多个处理程序(字典将抛出异常)
2. 你将单独拥有所有处理程序的注册,这将使你轻松找到哪个方法处理特定的参数类型。
3. 由于所有处理程序定位逻辑都没有重复,因此很容易修改它以处理继承类型,例如(我的代码不这样做,但你的代码确实这样做)。

1
+1 只是针对字典,我之前不知道这个东西。我要去看看它是否适合。看起来不错! :) - David
2
+1:我不喜欢在类型上使用switch/if-else,感觉不对。这种方式更加简洁。 - Matt Ellen
这并没有回答我的问题(实际上问题是如何切换控件的类型),但它确实为我的问题提供了更好的解决方案。由于其他答案在“类型切换”问题本身上并没有显示出任何特别的改进,因此我接受了这个答案。 - David

0

这是我会选择的解决方案:

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");
        }
    }
}

...但我相信有些人会觉得这太过了。个人而言,我喜欢switch语句的可读性,并尽可能使用它。此外,避免在“魔术字符串”上进行切换。尽可能使用const strings

如果您不介意我问一下,您到底想做什么?可能有更好的解决方案,不需要推断控件的类型。


0

好的,你不需要在第二个上嵌套使用else if

其次,为什么要把所有这些内容都放在一个方法中?最好的做法是,在调用此方法时,应该知道调用此方法的控件类型,然后只需针对该控件类型执行相应的DoSomething操作,而不是进行所有这些条件检查。


我有一个通用方法,适用于所有控件,我真的不想为每种控件类型重复编写代码,因为99%的函数代码对所有类型都是通用的(维护噩梦)。此外,我事先不知道控件的类型。它可以是任何东西,我无法预先知道控件的类型将是什么(这就是为什么这是一个静态函数,以控件作为参数,而不是在控件类中的实例函数)。 - David
我有一个通用的方法,我用于所有控件 - 不是的,否则你就不需要知道控件的类型!它无论如何都不能是一个通用的方法... - Ben
啊,是的,你说得对,“通用”的确不是我应该在那里使用的术语(这是因为英语不是我的母语 ;))。无论如何,我希望你能明白我的意思。在我的问题中,我将在未来的编辑中添加一些相关信息。 - David

0

我认为在这里使用 "is" 运算符是可以的。它更易读,而且如果控件不是你要找的,你也没有任何有用的备选路径。我不认为在这种情况下时间差异会很关键。

你可以通过从每个单独的 if 块返回来将 "else if" 替换为一系列普通的 "if",但这是个人风格选择。


0
最好将通用(与控件无关)功能重构为单独的函数,并在特定于控件的函数中包含特定于控件的功能。
然后,您可以在适当的情况下从特定于控件的函数中调用通用函数。

我编辑了我的问题,试图更好地解释我的观点。我的问题不在于可能拥有一个通用函数或使用特定类型的方法,而是在于如何切换控件类型。 - David

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接