如何存储 Switch 语句中的案例列表?

Posted

技术标签:

【中文标题】如何存储 Switch 语句中的案例列表?【英文标题】:How to store a list of Cases from Switch Statement? 【发布时间】:2012-12-24 01:05:28 【问题描述】:

在我的程序中,我有一个列表框,当用户双击一个对象时,它会查看 switch 语句以查看应该发生的事件。随着列表开始变大,我很好奇是否有办法避免在 2 个地方维护对象列表(一次在添加到列表框的列表中,一次在 switch 语句中。 有没有办法索引/读取/存储我的 switch 语句的各种案例,然后将它们作为对象添加到我的列表框?

示例:(不起作用,只是一个理论)

Switch (n)
ForEach (Case c in Cases)

   arrayCases.Add(c);

listbox.Items.AddRange(arrayCases);

编辑:

继续我现在的字典建议:

public void SetDictionary()
    
       //add entries to the dictionary
        dict["cat"] = new Action(Cat);
        dict["dog"] = new Action(Dog);

        //add each dictionary entry to the listbox.
        foreach (string key in dict.Keys)
        
            listboxTest.Items.Add(key);
                                    
    

     //when an item in the listbox is double clicked
     private void listboxTest_DoubleClick(object sender, EventArgs e)
     
         testrun(listboxCases.SelectedItem.ToString());             
     

     public void testrun(string n)
     
         //this is supposed to receive the item that was double clicked in the listbox, and run it's corresponding action as defined in the dictionary.
         var action = dict[n] as Action action();
     

我相信我上面的代码大部分是正确的并且我理解它,但是行动路线: var action = dict[n] as Action action();

显示一个错误,说明 'action' 需要一个 ';'。我这里的逻辑准确吗?如果是,为什么动作调用不正确?

【问题讨论】:

你的例子没有意义。 @Hogan,这是 Fuzz 想要实现的示例:枚举所有 switch 的情况... 我理解你的意思,你想以编程方式在你的 switch 中生成案例,但实际上你不需要这样做。将有另一种方法来执行您尝试执行的操作,例如获取列表框的选定索引。 @Hogan,阿列克谢是对的。这只是一个例子,完全是虚构的,显然行不通,这正是我认为我需要做的一个例子。 @FuzzEvans - 我从问题部分理解了你的问题。 (从我的回答中可以看出)。我只是指出你给出的例子不是 100% 清楚的。一切都好:) 【参考方案1】:

Dictionary<string, Action> 是避免的方法。 Dictionary.Keys 变为 ListBox.Items

switch(n) 变为

var action = dict[n] as Action
action();

【讨论】:

c# 在哪里?既不在问题中,也不在答案中。我错过了一个分号,但代码仅用于说明。 as c#, as 等价于is + 如果不为空则强制转换 你应该绑定ListBox,并添加键绑定数据源。添加喜欢foreach (var key in dictionary.Keys) data.Add(key); 我能够使用 foreach 循环将字典键添加到列表框中,如 Hogans 回答中所示,但我不清楚现在如何实现该操作。请参阅对 op 的修订。【参考方案2】:

我建议将您的操作移到单独的类中。为您的操作创建一个基类,如下所示。我为表单添加了一个字段,因为您可能必须与表单进行交互。如果需要,您还可以传入其他对象。

internal abstract class Operation

    protected readonly MyForm form = null;

    protected Operation(MyForm form)
    
        this.form = form;
    

    public abstract String DisplayName  get; 

    internal abstract void Execute();

然后为每个操作派生一个类。

internal sealed class DoThis : Operation

    internal DoThis(MyForm form) : base(form)  

    public override String DisplayName
    
        get  return "Do this!"; 
    

    internal override void Execute()
    
        // Code to do this. You can use this.form to interact with
        // your form from this operation.
    


internal sealed class DoSomethingElse : Operation

    internal DoSomethingElse(MyForm form) : base(form)  

    public override String DisplayName
    
        get  return "Do something else!"; 
    

    internal override void Execute()
    
        // Code to do something else.
    

现在您可以将所有操作添加到列表框中

this.lsitBox.Items.Add(new DoThis(this));
this.lsitBox.Items.Add(new DoSomethingElse(this));

并设置显示成员属性。

this.listBox.DisplayMember = "DisplayName";

最后在事件处理程序中执行选中的操作。

((Operation)this.listBox.SelectedItem).Execute();

这种模式在您的所有操作之间提供了清晰的分离,并使未来的扩展变得简单而干净。例如,如果您必须检查操作当前是否可用,您可以将属性 CanExecute 添加到所有操作。或者,如果您必须支持本地化,则可以轻松添加逻辑以在当前 UI 语言中显示操作名称。

另一个容易支持的场景是,如果您有一些所有操作通用的代码,例如日志记录、安全检查、性能测量等。

internal abstract class Operation

    protected readonly MyForm form = null;

    protected Operation(MyForm form)
    
        this.form = form;
    

    public abstract String DisplayName  get; 

    protected abstract void ExecuteCore();

    internal void Execute()
    
        Logger.Log("Executing operation " + this.DisplayName);

        try
        
            this.ExecuteCore();

            Logger.Log("Executing operation " + this.DisplayName + " succeeded.");
        
        catch (Exception exception)
        
            Logger.Log("Executing operation " + this.DisplayName + " failed.", exception);

            throw;
        
    

请注意,您现在必须覆盖 ExecuteCore() 而不是 Execute()

最后一个想法 - 使用接口 IOperation 代替或与抽象基类结合使用也可能会有所帮助。这消除了所有操作都从同一个基类继承的需要,因为这有时可能不方便。但我省略了这一点,以免过度设计。

【讨论】:

+1。完整方法的详细说明。我认为它对于基本切换来说有点过头了,但 OP 的案例听起来会受益于此处显示的更结构化的方法。 感谢您提供的信息。在这一点上,我显然已经不知所措,需要做更多的研究。不过还是谢谢你的推荐。【参考方案3】:

您不能*用普通代码枚举 caseswitch

您可以做的是将switch 替换为“动作名称”到“动作处理程序”的映射,然后您就可以将此映射重用于动作名称列表框列表。有关示例,请参阅 Tilak 的答案。

*) 如果你真的很好奇,你可以列举switch 的选项。 C# 代码被转换为 IL 并且 IL 可以用代码读取。因此,您可以获取方法的 IL,为 IL 编写(或获取现有的 - Parser for C#)解析器并在方法中找到 switch 的实现,选择所有情况。您甚至可以在构建时直接访问 C# 源代码 - 但它比 IL 解析更复杂。

【讨论】:

【参考方案4】:

是的,有一种方法可以通过制作 lambda 字典来做到这一点。

void Main()

  // set up your dictionary
  Dictionary<string,Action> myList = new Dictionary<string,Action> 
      "one", () =>  Console.WriteLine("One function");  ,
      "two",  () =>  Console.WriteLine("Two function"); ,
      "three", () =>  Console.WriteLine("Three function"); 
  ;

  // do a "switch" (that is invoke a function that corresponds to a name)
  myList["one"]();

  // loop the list of keys (that is get a list of all the names)
  foreach (string key in myList.Keys)
    Console.WriteLine(key);

这个程序的输出:

One function
one
two
three

另外请注意——你可以像这样动态地添加到这个“开关”中(这很酷,而且你不能用经典的 switch 语句来做。)

myList.Add("four",() =>  Console.WriteLine("Four function is dynamic"); );

【讨论】:

【参考方案5】:

在我看来,您的开关中的案例数量将会发生很大变化。如果这是真的,那么您可能需要考虑使用 switch 语句以外的机制。也许您想做一些像 Alexi Levenkov 建议的事情,然后迭代存储的动作名称列表并执行关联的处理程序。这样您就不必将动作名称添加到动作映射,然后将其添加到开关。

【讨论】:

以上是关于如何存储 Switch 语句中的案例列表?的主要内容,如果未能解决你的问题,请参考以下文章

如何在准备中的 switch 语句中使用可选绑定(segue :)

JavaScript Switch语句 - 单个案例中的同义词

为什么我可以将一个案例放在switch语句中的另一个案例中?

Matlab 中的类和 switch 语句

switch-case语句中的多个变量[重复]

Python中如何优雅地使用switch语句