【问题标题】:C# execute all cases in switch statementC#在switch语句中执行所有case
【发布时间】:2015-09-17 09:36:56
【问题描述】:

我有一个如下的 switch 语句:

switch(task)
    {
        case 1:
            print();
            break;
        case 2:
            save();
            break;
        case 3:
            sendmail();
            break;
    }

我需要一种方法来执行所有案例,这意味着如果任务是(全部) 我想打印、保存和发送邮件。 对给定案例进行一些修改是否可行 我知道,我可以这样:

case All:
    print();
    save();
    sendmail();
    break;

但正如我所说,我想知道,switch 语句中是否有办法执行所有情况。 提前致谢

【问题讨论】:

  • 也许您应该考虑使用标志枚举并创建一个Dictionary<enum, Action>。您可以对所有枚举值使用 foreach 循环并检查条目是否可用并执行该方法。所以在这种情况下需要切换。
  • 我不会发布这个作为答案。但是,当您为这些条件的流程添加复杂性时,它的可读性就会降低。例如,只需将其拆分并多次调用print()

标签: c# loops switch-statement case


【解决方案1】:

回答您的实际问题:

如果 switch 语句中有一种方法可以执行所有情况。

不是我能想到的干净方式。


我建议稍微改变一下模式,改为将task 改为flag enum

[Flags]
public enum TaskOptions 
{
    Print    = 1,
    Save     = 2,
    SendMail = 4,
    //Note that these numbers go up in powers of two
}

然后您可以执行以下操作:

task = TaskOptions.Print | TaskOptions.Save;
if (task.HasFlag(TaskOptions.Print))
{ 
    print();
}
if (task.HasFlag(TaskOptions.Save))
{ 
    save();
}
if (task.HasFlag(TaskOptions.SendMail))
{ 
    sendMail();
}

您不再需要明确地担心all


如果你想添加一个新选项

[Flags]
public enum TaskOptions 
{
    Print    = 1,
    Save     = 2,
    SendMail = 4,
    NewOption = 8,
}

task = TaskOptions.Print | TaskOptions.Save;
if (task.HasFlag(TaskOptions.Print))
{ 
    print();
}
if (task.HasFlag(TaskOptions.Save))
{ 
    save();
}
if (task.HasFlag(TaskOptions.SendMail))
{ 
    sendMail();
}
if (task.HasFlag(TaskOptions.NewOption))
{ 
    newOption();
}

Jaymee 要求澄清 |&

你能解释一下task = TaskOptions.Print | 的语法吗?任务选项。保存。不确定这里管子的使用,我以为是'或',但这里没有评价!?事实上,对于单个 & 符号也是如此 - 以前从未见过它以这种方式使用过

这些是位运算符。它们逐位比较两个数字并返回结果。

在我们的示例中,我们使用了 4 个标志,每个标志都由一个布尔值表示。一个布尔值可以用一位来表示。

让我们使用以下缩写:

Print = P
Save = S
SendMail = M
NewOption = N

8  4  2  1
N  M  S  P

我以task = TaskOptions.Print | TaskOptions.Save为例

0  0  0  1   P is declared as 1 in the enum. 
0  0  1  0   S is declared as 2 in the enum.
========== 
0  0  1  1   < I've "or'd" these numbers together. They now represent Print AND Save as one option. The number "3" (binary 0011) is equivalent to "print and save"

当我有“3”并且我想知道它是否包含特定标志时,我&amp; 带有该标志。

N  M  S  P
0  0  1  1  //This is P & S
0  0  0  1  //And we want to check if it has "P"
==========
0  0  0  1  < this returns non-zero. it contains the flag!

让我们为 N 做同样的事情

N  M  S  P
0  0  1  1  //This is P & S
1  0  0  0  //And we want to check if it has "N"
==========
0  0  0  0  < this returns zero. This state doesn't contain "N"

大卫·阿诺编辑

要添加到这个答案,而不是拥有一系列 ifs,可以使用 Dictionary 和 for 循环:

private readonly Dictionary<TaskOptions, Action> actions =
    new Dictionary<TaskOptions, Action>
    {
        { TaskOptions.Print, Print },
        { TaskOptions.Save, Save },
        { TaskOptions.SendMail , SendMail }
    };

...

var task = TaskOptions.Print | TaskOptions.Save;
foreach (var enumValue in Enum.GetValues(typeof(TaskOptions)).Cast<TaskOptions>())
{
    if (task.HasFlag(enumValue))
    {
        actions[enumValue]();
    }
}

【讨论】:

  • 感谢您的回答,但如果我有 5 个案例 :) 不是 3 个
  • 然后只需向枚举中添加更多案例:Option4=8, Option5=16 并重复 ifs 的模式
  • task.HasFlag(TaskOptions.NewOption)
  • 您可以通过将ifs 系列替换为Dictionary&lt;TaskOptions, Action&gt; 来改进此答案。除此之外,这是一个很好的答案。
  • 您可以像task = TaskOptions.Print | TaskOptions.Save | TaskOptions.SendMail 一样将所有任务连接在一起,或者使用数学:task = 2^(numofTasks)-1。在这种情况下,所有bits都是1,对应所有选项。
【解决方案2】:

尝试添加一个具有特殊行为的单独类,因为它比破解switch 语句更清晰:

class FallSwitch 
{
     SortedDictionary<int, Action> _cases = new SortedDictionary<int, Action>();

    public void AddCaseAction(int @case, Action action)
    {
        if (_cases.ContainsKey(@case)) throw ArgumentException("case already exists");
        _cases.Add(@case, action);
    }

    public void Execute(int startCase)
    {
        if (!_cases.ContainsKey(startCase)) throw ArgumentException("case doesn't exist");
        var tasks = _cases.Where(pair => pair.Key >= startCase);
        tasks.ForEach(pair => pair.Value());
    }
}

并像这样使用它:

var fs = new FallSwitch();
fs.AddCaseAction(0, () => Console.WriteLine("0"));
fs.AddCaseAction(1, () => Console.WriteLine("1"));
fs.AddCaseAction(2, () => Console.WriteLine("2"));
fs.AddCaseAction(6, () => Console.WriteLine("6"));

fs.Execute(1);

【讨论】:

    【解决方案3】:

    根据要求将评论作为答案。

    也许您应该考虑使用标志枚举并创建字典。您可以对所有枚举值使用 foreach 循环并检查条目是否可用并执行该方法。所以这种情况下需要switch。

    [Flags]
    public enum TaskOptions 
    {
        Print    = 1,
        Save     = 2,
        SendMail = 4
    }
    
    // In the clase where print(), save() and sendmail() are declared
    private readonly Dictionary<TaskOptions, Action> _tasks;
    
    public Ctor()
    {
        _tasks = new Dictionary<TaskOptions, Action>
            {
                { TaskOptions.Print, this.print },
                { TaskOptions.Save, this.save },
                { TaskOptions.SendMail, this.sendmail }
            };
    }
    
    public void Do(TaskOptions tasksToRun)
    {
        foreach (var action in from taskOption in Enum.GetValues(typeof(TaskOptions)).Cast<TaskOptions>()
                               where tasksToRun.HasFlag(taskOption)
                               orderby taskOption // only to specify it in the declared order
                               select this._tasks[taskOption])
        {
            action();
        }
    }
    

    但是您已经得到了一个使用干净策略模式执行相同操作的答案。这或多或少相同,但使用的是委托而不是类。

    【讨论】:

    • 好答案。不过有一件小事:我认为另一个答案使用的是 GoF 风格的命令模式,而不是策略模式。我也对“干净”提出异议,因为代表是这两种模式的更干净的实现。
    【解决方案4】:

    如您所知,在 C# 中,直通不会隐式发生,而是将 goto 扩展为允许案例标签成为目标标签,从而使直通可以是显式的。

    只需让您的“全部”值与第一个标签匹配,然后继续 goto 下一个标签(如果是该值):

    switch(task)
    {
      case 1: case 0;//I don't know what "all" is, so I'm using 0 here
        print();
        if (task == 0) goto case 2;
        break;
      case 2:
        save();
        if (task == 0) goto case 3;
        break;
      case 3:
        sendmail();
        break;
    }
    

    【讨论】:

    • @modiX 从不使用标准成语作为语言中的失败者?为什么? (请告诉我,你不是基于 Dijkstra 在 1968 年写的东西。这四个字母的这种特殊用法在 1968 年并不存在)。
    • 确实,我没有使用GOTO,我使用的是goto...
    • @JonHanna - Dijkstra 所说的话仍然有效。用goto 控制流量太可怕了。
    • case 2 中的 if task==0 条件永远不会评估为真。
    • 这是一个可怕的语言功能 IMO
    【解决方案5】:

    您可以尝试更面向对象的方法。定义一组类,每个类负责执行一种类型的任务。当您想要运行多个任务时,只需将单个任务排队即可。使用 switch 语句非常不灵活。想象有一天添加第 4 个任务,然后还想要一个选项来按顺序运行任务 1,2 和 4。

     RunTasks(new[] {new PrintTask(), new SaveTask(), new SendMailTask()});
    
     void RunTasks(int[] tasks)
     {
         foreach (var task in tasks)
         {
             task.Run();
         }
     }
    
     interface ITask
     {
         void Run();
     }
    
     class PrintTask : ITask
     {
          public void Run()
          {
              ....
          }
      }
    
      // etc...
    

    【讨论】:

    • 您在这里重新发明了命令模式轮。 C# 已经有了Action,它更整齐地覆盖了模式,而无需创建大量重复性很强的类。
    【解决方案6】:

    你可以像这样创建一个新案例

    switch(task)
    {
        case 1:
            print();
            break;
        case 2:
            save();
            break;
        case 3:
            sendmail();
            break;
        case 4:
            print();
            save();
            sendmail();
            break;
    }
    

    或者,您可以设置默认案例以执行所有案例

     switch(task)
    {
        case 1:
            print();
            break;
        case 2:
            save();
            break;
        case 3:
            sendmail();
            break;
        default:
            print();
            save();
            sendmail();
            break;
    }
    

    这两种方法都不是很简洁,但我认为它们可能是解决您问题的最简单的方法。

    【讨论】:

      猜你喜欢
      • 2014-03-27
      • 1970-01-01
      • 2015-07-16
      • 2020-09-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多