用图案替换开关/外壳

Chr*_*ris 8 c# asp.net design-patterns

在后面的代码中,我的代码与此示例非常相似三次.每次交换机切换到发送给它的选项.每次案例中的代码完全相同,除了基于案例的参数.使用开关/盒和方法最好的方法吗?我是否应该考虑使用某种类型的设计模式来避免重复开关/案例结构?

string option = dropDownList.SelectedValue.ToString();
switch (option.ToUpper())
{
    case "ALPHA":
        // do repeative code method here; only change is a parameter
        break;
    case "BRAVO":
        // do repeative code method here; only change is a parameter
        break;
    case "CHARLIE":
        // do repeative code method here; only change is a parameter
        break;
    case "DELTA":
        // do repeative code method here; only change is a parameter
        break;
    default:
        break;
}
Run Code Online (Sandbox Code Playgroud)

Hen*_*man 11

您可以构造要转换string为的表parameter value.

var lookup = new Dictionary<string, ParaType> ()  
{
    { "ALPHA", a  },
    { "BETA", b },
    ....
};

ParaType para;
if (lookup.TryGetValue(option, out para))   // TryGetValue, on popular request
{       
   // do repeative code method here; with para
}
Run Code Online (Sandbox Code Playgroud)

  • 实际上,如果你的交换机中有超过6个字符串,那么查找解决方案将是IL在编译后将执行的操作(实际上它是TryGetValue,但想法是一样的) (3认同)

Aar*_*ght 11

编译器非常擅长优化开关/案例结构; CLR可能会把它变成查询表或类似的快速,所以手动滚动自己的版本,如Henk Holterman建议不是我建议的.在选择最佳算法时,CLR可以做得更好.

如果它是一个优雅或可维护性的问题,并且你有几个关于同一个类执行类似功能的开关/案例,那么改进它的一种方法是将与单个"case"相关的所有功能封装到它自己的类实例中,像这样:

class MyOption
{
    public static readonly MyOption Alpha = new MyOption(1, 10, "Alpha Text");
    public static readonly MyOption Bravo = new MyOption(2, 100, "Bravo Text");
    public static readonly MyOption Charlie = new MyOption(3, 1000, "Charlie Text");
    // ... Other options ...
    public static readonly MyOption Default = new MyOption(0, 0, null);

    public MyOption(int id, int value, string text)
    {
        this.ID = id;
        this.Value = value;
        this.Text = text;
    }

    public int ID { get; private set; }
    public int Value { get; private set; }
    public string Text { get; private set; }
}
Run Code Online (Sandbox Code Playgroud)

然后在你的班级/控制/页面中:

static MyOption GetOption(string optionName)
{
    switch (optionName)
    {
        case "ALPHA":
            return MyOption.Alpha;
        case "BRAVO":
            return MyOption.Bravo;
        case "CHARLIE":
            return MyOption.Charlie;
        // ... Other options ...
        default:
            return MyOption.Default;
    }
}

private MyOption GetOptionFromDropDown()
{
    string optionName = GetOptionNameFromDropDown();
    return GetOption(optionName);
}

private string GetOptionNameFromDropDown()
{
    // ... Your code ...
}
Run Code Online (Sandbox Code Playgroud)

之后,您可以开始制作事件和其他方法:

private void control1_SomeEvent(object sender, EventArgs e)
{
    MyOption option = GetOptionFromDropDown();
    DoSomething(option.ID);
}

private void control2_SomeEvent(object sender, EventArgs e)
{
    MyOption option = GetOptionFromDropDown();
    DoSomethingElse(option.Value);
}
Run Code Online (Sandbox Code Playgroud)

当然,如果你想将几个这样的开关/案例重构为一个,这只是一个有用的模式.如果您只有一个开关/外壳,那么您最终会以这种方式获得更多代码,所以请不要管它!

提高可维护性的其他可能性包括:

  • 将字符串更改为枚举类型(使用Enum.Parse转换optionName);
  • 将所有MyOption/GetOption内容移动到它自己的类中(如果你有几个类/控件/页面都需要在同一组选项上运行);
  • 如果您确实需要为每个方法调用不同的方法,请将方法委托添加到MyOption类;
  • 如果可能,让DropDownList或其他控件存储对MyOption实例的直接引用.

就是这样.它编写简单,易于理解,易于维护,如果您有大量的开关/案例结构,它将节省您的时间,并且它仍然允许CLR执行最佳的优化.唯一的成本是保存那些只读字段所需的少量内存.