有没有一种更有效的方法可以根据选中的复选框来做事?

Is there a more efficient way to do things according to which checkboxes are checked?

我自己最近刚遇到这个问题,搜索这个问题还没有结果...

假设我有四个复选框,作为示例,我想根据随时选中的复选框来做某些事情...

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 个更多的复选框,或者要比较 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);
    }
}

所以,我的问题是... 是否有更好的(或更有效的,或可以减少代码行数)做事的方式 checkbox/checkboxes is/are 检查了吗?还是我们真的坚持使用这种方法(或这种方法的变体)?

请注意,对于此示例,无论您根据选中的复选框做什么...都不能简单地是 "added on" 或 "appended".

还应注意,switch-case 方法或多或少与此方法相同...因此很可能不会有什么不同。也已经concluded elsewhere that if-statements are more efficient that switch-case.

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

例如

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 之前已被初始化(可能是 class 的 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 或查找丢失的一件事是可读性,因为您基本上最终将复选框值转换为位掩码并根据最终 "magic" 整数值执行操作。当然,如果复选框实际上是 编号的 ,那么可能很容易跟踪哪个是哪个位。但是,如果名称类似于 ApplesCheckBoxSeedsCheckBoxSoilCheckBox,您将很难将其与整数位掩码匹配。

您可以通过标志枚举取回其中的一些内容。例如,搭载 :

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