是否有一种更有效的方法来根据复选框被选中来做事情

本文关键字:复选框 方法 一种 有效 是否 | 更新日期: 2023-09-27 18:11:27

我自己最近也遇到了这个问题,搜索这个问题还没有结果…

假设我有四个复选框,作为一个例子,我想根据它们中的哪个在任何时候都被选中来做某些事情…

if (CB1.Checked)
{
    //Do things here if only checkbox 1 is checked.
}
else if (CB2.Checked)
{
    //Do things here if only checkbox 2 is checked.
}
else if (CB3.Checked)
{
    //Do things here if only checkbox 3 is checked.
}
else //if (CB4.Checked)
{
    //Do things here if only checkbox 4 is checked.
}

我相信大多数人会倾向于使用类似于上面的示例代码片段,或者它的变体。看起来很简单,对吧?但是如果……你不是只选中了一个复选框吧?

if (CB1.Checked && CB2.Checked)
{
    //Do things here if only checkbox 1 & 2 is checked.
}
else if (CB2.Checked && CB3.Checked)
{
    //Do things here if only checkbox 2 & 3 is checked.
}
else if (CB3.Checked && CB1.Checked)
{
    //Do things here if only checkbox 3 & 1 is checked.
}
else if (CB4.Checked && CB1.Checked)
{
    //Do things here if only checkbox 4 & 1 is checked.
}
else if (CB4.Checked && CB2.Checked)
{
    //Do things here if only checkbox 4 & 2 is checked.
}
else //if (CB4.Checked && CB3.Checked)
{
    //Do things here if only checkbox 4 & 3 is checked.
}

可以看出……if-else语句的数量增加了……如果你想比较更多的复选框,或者4个复选框中的更多复选框。这可能会使事情复杂化,(可能)大多数程序员无法避免它。

我还应该提到,我知道在给定的时间有多少复选框被选中,这要归功于这段代码:
private int GetNumberOfCheckboxesChecked()
{
    int NumberofCheckBoxesChecked = 0;
    foreach (Control c in groupBox1.Controls)
    {
        if ((c is CheckBox) && ((CheckBox)c).Checked)
            NumberofCheckBoxesChecked++;
    }
    return NumberofCheckBoxesChecked;
}

它们还需要始终选中其中一个复选框,因为每个复选框的checkchanged事件调用以下代码:

private void OneAtLeast(object originalSender)
{
    CheckBox tempCB = (CheckBox)originalSender;
    if (!CB1.Checked && !CB2.Checked && !CB3.Checked && !CB4.Checked)
    {
        tempCB.Checked = true;
        MessageBox.Show("You must select at least one option!", "Invalid Operation", MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

所以,我的问题是……是否有更好的(或更有效的,或可以减少代码行)的方式来做事情根据哪个复选框/复选框被选中?还是我们真的坚持这种方法(或这种方法的变体)?

请注意,在这个例子中,无论你要做什么,根据哪个复选框被选中…

还应该注意的是,switch-case方法将或多或少与此方法相同…所以很可能不会有什么不同。在其他地方也得出结论,if语句比switch-case更有效。

是否有一种更有效的方法来根据复选框被选中来做事情

您可以创建一个字典,将int s映射到Action s或Func s(或任何其他合适的),然后使用复选框在整数中设置位。一旦计算出整数,就在字典中查找它并分派给该方法。字典只能初始化一次。

int option = 0;
if(CB1.Checked) option = option | 1;
if(CB2.Checked) option = option | 2;
if(CB3.Checked) option = option | 4;
if(CB4.Checked) option = option | 8;
if(!lookup.HasKey(option))
    throw new NotSupportedException("I didn't expect that combination of options");
lookup[option]();

lookup先前已经初始化(可能是类的static成员)

lookup = new Dictionary<int,Action>();
lookup.Add(0,DoNothingNoOptionsSet);
lookup.Add(1,DoJustCB1);
lookup.Add(2,DoJustCB2);
lookup.Add(3,DoCB1AndCB2ButNeverCB4);
/* etc, for other valid options */

这也使您有机会对执行的每个函数应用描述性名称,将它们移出到单独的函数中,然后将公共功能的区域组合为更小的辅助函数。

我不确定你能对分支的数量做多少。如果逻辑允许,可以通过嵌套if语句来处理其中的一些问题。但这只在某些情况下有用,在其他情况下会使事情变得更糟。

如果你真的有16个不同的,不相关的4个复选框选项,你最好使用switch,或查找字典@Damien_The_Unbeliever建议。switch或查找丢失的一件事是可读性,因为您基本上最终将复选框值转换为位掩码,并根据最终的"魔法"整数值执行操作。当然,如果复选框实际上是编号的,那么跟踪哪个是哪个位可能很容易。但是,如果名称是ApplesCheckBox, SeedsCheckBox, SoilCheckBox之类的,那么您将很难将其与整数位掩码匹配。

您可以使用flags枚举返回其中的一部分。例如,借用@Damien_The_Unbeliever的答案:

[Flags]
enum CheckboxActions
{
    None = 0,
    CheckBox1 = 1,
    CheckBox2 = 2,
    CheckBox3 = 4,
    CheckBox4 = 8,
    DoJustCB1 = 1,
    DoJustCB2 = 2,
    DoCB1AndCB2ButNeverCB4 = 3
}

,

var option = CheckboxActions.None;
if(CB1.Checked) option = option | CheckboxActions.CheckBox1;
if(CB2.Checked) option = option | CheckboxActions.CheckBox2;
if(CB3.Checked) option = option | CheckboxActions.CheckBox3;
if(CB4.Checked) option = option | CheckboxActions.CheckBox4;

使用它,例如在开关中:

switch (option)
{
    case CheckboxActions.None:
        DoNothingNoOptionsSet();
        break;
    case CheckBoxActions.DoJustCB1:
        DoJustCB1();
        break;
    case CheckBoxActions.DoJustCB2:
        DoJustCB2();
        break;
    case CheckBoxActions.DoCB1AndCB2ButNeverCB4:
        DoCB1AndCB2ButNeverCB4();
        break;
}

或者只是设置一个Dictionary<CheckboxActions, Action>并将其用作查找。

您可以为枚举值使用适当的名称,这样您就可以确切地知道发生了什么,并且复选框的顺序由枚举值CheckboxActions.CheckBox1CheckboxActions.CheckBox4封装。作为奖励,与使用普通的旧int相比,将CheckboxActions传递给不同的类和方法可以获得更多的类型安全性。


实际上,在这个过程中,我刚刚了解到可以从同一枚举中引用枚举值。因此,您可以完全删除魔术值,上面的代码变成(是的,这可以编译):

[Flags]
enum CheckboxActions
{
    None = 0,
    CheckBox1 = 1,
    CheckBox2 = 2,
    CheckBox3 = 4,
    CheckBox4 = 8,
    DoJustCB1 = CheckboxActions.CheckBox1,
    DoJustCB2 = CheckboxActions.CheckBox2,
    DoCB1AndCB2ButNeverCB4 = CheckboxActions.CheckBox1 | CheckboxActions.CheckBox2
}