我正在编写一个Windows窗体应用程序,该应用程序具有4个可以同时选中的复选框。我想检查一下选中了哪些框,并根据选中的框做特定的逻辑。问题是,如果我最终尝试编写代码来检查复选框的每个可能组合,则必须编写一长串if else语句列表,而这不会检查是否只有一个被选中。有没有更优雅的方法来检查是否选中了多个复选框?还是我坚持使用if-else逻辑?
//This is the type of code I am trying to avoid
//check all the conditions
//check1 is a check to add keys
//check2 is to create docs
//check3 is to create keys
//check4 is to reorder keys
//createKeys() creates keywords
//createDocs() creates documents
//addKeys() adds keywords
//reorderKeys reorders the keywords
if(check1 && check2 && check3 && check4){
createKeys();
createDocs();
addKeys();
reorderKeys();
}
else if(check1 && check2 && check3){
createKeys();
createDocs();
addKeys();
}
else if(check1 && check2 && check4){
createDocs();
addKeys();
reorderKeys();
}
else if(check1 && check2){
createDocs();
addKeys();
}
else if(check1){
addKeys();
}
else if(check2 && check3 && check4){
createKeys();
createDocs();
reorderKeys();
}
else if(check2 && check4){
createDocs();
reorderKeys();
}
else if(check2){
createDocs();
}
else if(check3 && check4){
createKeys();
reorderKeys();
}
else if(check3){
createKeys();
}
else if(check4){
reorderKeys();
}
代码更新更加具体
答案 0 :(得分:5)
if/else
的替代方法是方法表。您可以创建一个词典并将所有代码放置为Action
var methodTable = new Dictionary<(bool, bool, bool, bool), Action>
{
[(false, false, false, true)] = () => { /* do something */ },
[(false, false, true, false)] = () => { /* do something else */ },
[(false, false, true, true)] = SomeClassMethod,
// and so on
};
现在您可以单行从if/else
块中调用代码:
methodTable[(check1, check2, check3, check4)].Invoke();
编辑
如果复选框的某些组合不希望您处理,则必须选择:
向methodTable
添加空处理程序,例如:
[(false, false, false, true)] = () => {}, // do nothing
或者(对我来说更好)使用TryGetValue
方法:
if (methodTable.TryGetValue((check1, check2, check3, check4), out var method))
method.Invoke();
答案 1 :(得分:2)
使用枚举:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication46
{
public partial class Form1 : Form
{
enum CheckBoxEnum
{
CHECKBOXNONE = 0,
CHECKBOX1 = 1,
CHECKBOX2 = 2,
CHECKBOX3 = 4,
CHECKBOX4 = 8,
}
List<CheckBox> checkboxes;
CheckBoxEnum boxEnum = CheckBoxEnum.CHECKBOXNONE;
public Form1()
{
InitializeComponent();
checkboxes = new List<CheckBox>() { checkBox1, checkBox2, checkBox3, checkBox4 };
foreach (CheckBox box in checkboxes)
{
box.Click += new EventHandler(box_Click);
}
switch (boxEnum)
{
case CheckBoxEnum.CHECKBOX1 | CheckBoxEnum.CHECKBOX3 :
break;
}
}
private void box_Click(object sender, EventArgs e)
{
CheckBox box = sender as CheckBox;
if (box.Checked)
{
boxEnum |= (CheckBoxEnum)(1 << checkboxes.IndexOf(box));
}
else
{
boxEnum &= ~(CheckBoxEnum)(1 << checkboxes.IndexOf(box));
}
}
}
}
答案 2 :(得分:1)
我们将使用数字集来表示已设置的复选框,因此{1, 3}
的意思是check1 && check3
。
如果所有逻辑都是完全分开的,即对{a_1, a_2, ..., a_n}
和{a_1, a_2, ..., a_n, x}
的{{1}}和a_1, ..., a_n
采取的行动之间没有联系,那么显然您需要每个组合都有x
个单独的过程。这意味着无论哪种方式,我们都必须分支到2^n
个不同的代码流中。因此,问题归结为我们是否可以以较少的代码消耗方式决定使用哪个集合。
显然,您需要以某种方式将行为绑定到不同的集合。这意味着无论我们做什么,在假设我们可以拥有2^n
个完全不相关的过程(每个集合一个)的前提下,您仍然必须至少具有2^n
条语句来将特定集合链接到特定程序。因此,简短的答案是否定的,即使您摆脱了2^n
语句,例如将支票编码为位序列,然后将这些序列映射到过程,您也会在这里摆脱逻辑,但在其他地方在代码中,您必须具有以下映射:
if/else
所以仍然有Dictionary<string, Action> logic = new Dictionary<int[], Action>();
logic["{1}"] = DoStuffFor1;
logic["{2}"] = DoStuffFor2;
...
logic["{N}"] = DoStuffForN;
logic["{1, 2}"] = DoStuffFor12;
logic["{1, 3}"] = DoStuffFor13;
...
var checkboxSet = Encode(check1, check2, ..., checkN); // Implementation of Encode should be obvious.
logic[checkboxSet].Invoke();
条语句。因此,在一般情况下,无法减少编写的行数,但您可能会发现代表映射比2^n
语句群更为优雅。
但是,如果不同集合的复选框的逻辑以某种方式互连(在任何合理的实际应用中都是这种情况),则可能会折叠if/else
语句以缩短代码。但是,如果没有更多有关程序实际逻辑的详细信息,就无法说清。
编辑:
实际上,现在我想到了,您可以有一种非常复杂的方法,可以在运行时使用反射来解析方法名称,并为方法使用命名约定,以便所有处理程序的名称都是明确的(例如,处理if
的方法将被命名为{1, 42, 100}
等),您可以获取名称与已选中复选框等效的方法并调用它。但是,仍然需要您编写DoStuffFor_1_42_100
过程。另外,我认为我们可以一致同意,使用反射来实现这一目标并依靠命名约定来使代码正常工作是一个糟糕的主意。
答案 3 :(得分:1)
(我知道您已经有了答案,可以考虑在其他情况下将此选项作为替代方案)。
您可以将特定操作与某个控件或一组控件相关联,然后在需要时执行所有相关的操作,并按顺序触发这些操作。如果需要,请按照此处的顺序进行操作。
例如,您的 createKeys()
方法在被选择时优先级更高, createDocs()
一级更少,但是,如果它们也处于活动状态,则必须在其他命令之前执行。
因此,使用 manager 类,您可以将所有动作(方法调用)与Control的状态相关联,定义Action优先级,然后在有条件的情况下在一次调用中执行所有动作更改(选中一个复选框,单击一个按钮等)。
使用 ActionManager
类,您可以将Actions与Controls关联,将它们添加到类中,然后调用 PerformActions()
执行所有活动方法:
ActionManager.Actions.AddRange(new[] {
new ActionManager.ActionControl(() => CreateKeys(), this.chkCreateKeys, 1),
new ActionManager.ActionControl(() => CreateDocs(), this.chkCreateDocs, 2),
new ActionManager.ActionControl(() => AddKeys(), this.chkAddKeys, 3),
new ActionManager.ActionControl(() => ReorderKeys(), this.chkReorderKeys, 4)
});
需要时,只需致电:
ActionManager.PerformActions();
,所有方法都将执行。
这是它在CheckedChanged
事件(可以是按钮单击或其他任何事件)上的执行方式:
ActionManager
类:
public class ActionManager
{
public static List<ActionControl> Actions { get; set; } = new List<ActionControl>();
public static void PerformActions() {
var sequence = Actions.Where(c => c.Control.Checked).OrderBy(a => a.ActionPriority);
foreach (var action in sequence) {
action.Action?.Invoke();
}
}
public class ActionControl
{
public ActionControl(Action action, CheckBox control, int priority) {
this.Action = action;
this.Control = control;
this.ActionPriority = priority;
}
public Action Action { get; }
public CheckBox Control { get; }
internal int ActionPriority { get; }
}
}
答案 4 :(得分:0)
对于4个复选框,您可以执行以下操作:
将它们添加到数组中
bool[] checkboxes = { check1, check2, check3, check4 };
然后通过其索引选择10个幂,因此您可以使用其Sum
来获取每个值的唯一值:
var selected = checkboxes
.Reverse() // reversing to get result with correct order
.Select((s, i) => new { IsChecked = s, Index = Math.Pow(10, i) })
.Where(w => w.IsChecked)
.Sum(s => s.Index);
因此您将获得1100
的{{1}}或true, true, false, false
的{{1}},等等。
,您可以将101
用于所有可能性,例如:
false, true, false, true
并这样称呼它:
Dictionary<double, Action>