[C#] 用一种更优美的方式来替换掉又多又长的switch-case代码段
switch-case语句是我们编码过程中常用的一种分支语句。然而正所谓成也萧何败萧何,每当我们向一个已经拥有了成百上千行的switch-case代码段中添加新的case分支的时候,我们是否有过为代码的可读性和可维护性不断下降而头疼烦恼呢。
事实上,我们可以有很多方法来避免出现这种分支有多又长的switch-case代码段,从而写出更优美的代码。在.Net中我们可以非常简单地分解switch-case中的代码。
下面选择了一个比较常见的例子:模块采用switch-case来处理接收到的Command
假设我们现在定义了以下10个CommandID。
/// <summary>
/// Definition of commands.
/// </summary>
enum CommandID
{
Abs = ,
Sin = ,
Sinh = ,
Asin = ,
Tan = ,
Tanh = ,
Atan = ,
Cos = ,
Cosh = ,
Acos =
}
CommandID
下面我们定义了CommandHandler1类来处理这些命令。该类采用switch-case语句分别处理不同的CommandID。我们可以将每个CommandID的处理逻辑封装在各个函数中(这里偷懒,假设Math里面定义的几个方法就是我们封装的处理逻辑),然后在每个case中调用相应的函数即可。
class CommandHandler1
{
/// <summary>
/// Handle the command.
/// </summary>
/// <param name="cmdID">The command ID of the command to be handled.</param>
/// <param name="cmdArg">The command argument of the command to be handled.</param>
/// <returns>The handle result.</returns>
public double HandleCommand(CommandID cmdID, double cmdArg)
{
double retValue;
switch (cmdID)
{
case CommandID.Abs:
retValue = Math.Abs(cmdArg);
break;
case CommandID.Sin:
retValue = Math.Sin(cmdArg);
break;
case CommandID.Sinh:
retValue = Math.Sinh(cmdArg);
break;
case CommandID.Asin:
retValue = Math.Asin(cmdArg);
break;
case CommandID.Tan:
retValue = Math.Tan(cmdArg);
break;
case CommandID.Tanh:
retValue = Math.Tanh(cmdArg);
break;
case CommandID.Atan:
retValue = Math.Atan(cmdArg);
break;
case CommandID.Cos:
retValue = Math.Cos(cmdArg);
break;
case CommandID.Cosh:
retValue = Math.Cosh(cmdArg);
break;
case CommandID.Acos:
retValue = Math.Acos(cmdArg);
break;
default:
retValue = this.HandleDefaultCommand(cmdArg);
break;
} return retValue;
} /// <summary>
/// Handle the default command.
/// </summary>
/// <param name="cmdArg">The command argument of the default command.</param>
/// <returns>The handle result.</returns>
private double HandleDefaultCommand(double cmdArg)
{
return ;
}
}
CommandHandler1
在CommandHandler1中,我们如果新增了一个命令,那么就需要增加一个处理新命令的方法,同时修改HandleCommand方法体,在其中添加一个case分支并调用新增的方法。
下面利用字典和委托CommandHandler1里面的switch-case代码段。我们新定义了一个类CommandHandler2,将处理每个CommandID的委托方法保存在一个字典表中(cmdHandlers),在HandleCommand方法体中,通过cmdID找到对应的委托方法来处理响应的cmdID。
class CommandHandler2
{
/// <summary>
/// The dictionary contains all the command handlers to handle the commands.
/// </summary>
private Dictionary<CommandID, Func<double, double>> cmdHandlers = new Dictionary<CommandID, Func<double, double>>
{
{CommandID.Abs, Math.Abs}, {CommandID.Sin, Math.Sin}, {CommandID.Sinh, Math.Sinh}, {CommandID.Asin, Math.Asin},
{CommandID.Tan, Math.Tan}, {CommandID.Tanh, Math.Tanh}, {CommandID.Atan, Math.Atan}, {CommandID.Cos, Math.Cos},
{CommandID.Cosh, Math.Cosh}, {CommandID.Acos, Math.Acos}
}; /// <summary>
/// Handle the command.
/// </summary>
/// <param name="cmdID">The command ID of the command to be handled.</param>
/// <param name="cmdArg">The command argument of the command to be handled.</param>
/// <returns>The handle result.</returns>
public double HandleCommand(CommandID cmdID, double cmdArg)
{
var cmdHandler = this.cmdHandlers.ContainsKey(cmdID) ? this.cmdHandlers[cmdID] : this.HandleDefaultCommand;
return cmdHandler(cmdArg);
} /// <summary>
/// Handle the default command.
/// </summary>
/// <param name="cmdArg">The command argument of the default command.</param>
/// <returns>The handle result.</returns>
private double HandleDefaultCommand(double cmdArg)
{
return ;
}
}
CommandHandler2
当我们新增一个命令时,只需要增加一个处理新命令的方法,同时将这个新命令及其对应的委托方法添加到字典表中即可。
下面我们来看一下这两种方法的性能。在测试性能时,我们将所有的cmd处理方法全都替换成了HandleDefaultCommand。
class CommandHandlerTest1
{
/// <summary>
/// Handle the command.
/// </summary>
/// <param name="cmdID">The command ID of the command to be handled.</param>
/// <param name="cmdArg">The command argument of the command to be handled.</param>
/// <returns>The handle result.</returns>
public double HandleCommand(CommandID cmdID, double cmdArg)
{
double retValue;
switch (cmdID)
{
case CommandID.Abs:
retValue = this.HandleDefaultCommand(cmdArg);
//retValue = Math.Abs(cmdArg);
break;
case CommandID.Sin:
retValue = this.HandleDefaultCommand(cmdArg);
//retValue = Math.Sin(cmdArg);
break;
case CommandID.Sinh:
retValue = this.HandleDefaultCommand(cmdArg);
//retValue = Math.Sinh(cmdArg);
break;
case CommandID.Asin:
retValue = this.HandleDefaultCommand(cmdArg);
//retValue = Math.Asin(cmdArg);
break;
case CommandID.Tan:
retValue = this.HandleDefaultCommand(cmdArg);
//retValue = Math.Tan(cmdArg);
break;
case CommandID.Tanh:
retValue = this.HandleDefaultCommand(cmdArg);
//retValue = Math.Tanh(cmdArg);
break;
case CommandID.Atan:
retValue = this.HandleDefaultCommand(cmdArg);
//retValue = Math.Atan(cmdArg);
break;
case CommandID.Cos:
retValue = this.HandleDefaultCommand(cmdArg);
//retValue = Math.Cos(cmdArg);
break;
case CommandID.Cosh:
retValue = this.HandleDefaultCommand(cmdArg);
//retValue = Math.Cosh(cmdArg);
break;
case CommandID.Acos:
retValue = this.HandleDefaultCommand(cmdArg);
//retValue = Math.Acos(cmdArg);
break;
default:
retValue = this.HandleDefaultCommand(cmdArg);
break;
} return retValue;
} /// <summary>
/// Handle the default command.
/// </summary>
/// <param name="cmdArg">The command argument of the default command.</param>
/// <returns>The handle result.</returns>
private double HandleDefaultCommand(double cmdArg)
{
return ;
}
} class CommandHandlerTest2
{
/// <summary>
/// The dictionary contains all the command handlers to handle the commands.
/// </summary>
//private Dictionary<CommandID, Func<double, double>> cmdHandlers = new Dictionary<CommandID, Func<double, double>>
//{
// {CommandID.Abs, Math.Abs}, {CommandID.Sin, Math.Sin}, {CommandID.Sinh, Math.Sinh}, {CommandID.Asin, Math.Asin},
// {CommandID.Tan, Math.Tan}, {CommandID.Tanh, Math.Tanh}, {CommandID.Atan, Math.Atan}, {CommandID.Cos, Math.Cos},
// {CommandID.Cosh, Math.Cosh}, {CommandID.Acos, Math.Acos}
//};
private Dictionary<CommandID, Func<double, double>> cmdHandlers; public CommandHandlerTest2()
{
cmdHandlers = new Dictionary<CommandID, Func<double, double>>
{
{CommandID.Abs, this.HandleDefaultCommand}, {CommandID.Sin, this.HandleDefaultCommand},
{CommandID.Sinh, this.HandleDefaultCommand}, {CommandID.Asin, this.HandleDefaultCommand},
{CommandID.Tan, this.HandleDefaultCommand}, {CommandID.Tanh, this.HandleDefaultCommand},
{CommandID.Atan, this.HandleDefaultCommand}, {CommandID.Cos, this.HandleDefaultCommand},
{CommandID.Cosh, this.HandleDefaultCommand}, {CommandID.Acos, this.HandleDefaultCommand}
};
} /// <summary>
/// Handle the command.
/// </summary>
/// <param name="cmdID">The command ID of the command to be handled.</param>
/// <param name="cmdArg">The command argument of the command to be handled.</param>
/// <returns>The handle result.</returns>
public double HandleCommand(CommandID cmdID, double cmdArg)
{
var cmdHandler = this.cmdHandlers.ContainsKey(cmdID) ? this.cmdHandlers[cmdID] : this.HandleDefaultCommand;
return cmdHandler(cmdArg);
} /// <summary>
/// Handle the default command.
/// </summary>
/// <param name="cmdArg">The command argument of the default command.</param>
/// <returns>The handle result.</returns>
private double HandleDefaultCommand(double cmdArg)
{
return ;
}
} class Program
{
static void Main(string[] args)
{
List<CommandID> cmdList = new List<CommandID>()
{
CommandID.Abs, CommandID.Sin, CommandID.Sinh, CommandID.Asin, CommandID.Tan,
CommandID.Tanh, CommandID.Atan, CommandID.Cos, CommandID.Cosh, CommandID.Acos
}; Stopwatch watch = new Stopwatch(); watch.Start();
CommandHandlerTest1 test1 = new CommandHandlerTest1();
for (int i = ; i < ; i++)
{
for (int j = ; j < ; j++)
{
test1.HandleCommand(cmdList[j], 0.1);
}
} watch.Stop();
Console.WriteLine(watch.ElapsedMilliseconds); watch.Reset();
watch.Start();
CommandHandlerTest2 test2 = new CommandHandlerTest2();
for (int i = ; i < ; i++)
{
for (int j = ; j < ; j++)
{
test2.HandleCommand(cmdList[j], 0.1);
}
} watch.Stop();
Console.WriteLine(watch.ElapsedMilliseconds); Console.ReadLine();
}
}
Performance Test
原本认为采用字典表+委托的方法性能应该比switch-case高,但测试结果却令人失望(CommandHandler1比CommandHandler2反而高了近50%)。分析这里面可能的原因:
1. 通过委托调度处理方法比直接调用方法效率相对较低;
2. 编译器对switch-case的代码进行了一定的优化。
好在一般对这里的性能要求不是很高,100W次平均下来相差零点几微秒,而在又多又长的switch-case代码段中,一般是可以接受的。而对于比较简短的switch-case代码段,也就没有必要采用第二种方式去替换了。
当然这里所举的处理Command的例子,有更好的解决方案。由于这篇文章只是讲述如何用一种更优美的方式来替代switch-case,因此就不再详细描述了。
[C#] 用一种更优美的方式来替换掉又多又长的switch-case代码段的更多相关文章
- 少年,是时候换种更优雅的方式部署你的php代码了
让我们来回忆下上次你是怎么发布你的代码的: 1. 先把线上的代码用ftp备份下来 2. 上传修改了的文件 3. 测试一下功能是否正常 4. 网站500了,赶紧用备份替换回去 5. 替换错了/替换漏了 ...
- Java代码消除switch/case,if/else语句的几种实现方式
转自:https://my.oschina.net/stefanzhlg/blog/372413 我们在平时的编码中,我们经常会遇到这样的情况: 使用过多的switch/case 或者 if else ...
- angularjs中向html页面添加内容节点元素代码段的两种方法
第一种方式:原生JS向html页面添加内容节点元素代码段: <!DOCTYPE html> <html> <head> <meta charset=" ...
- C#不用union,而是有更好的方式实现 .net自定义错误页面实现 .net自定义错误页面实现升级篇 .net捕捉全局未处理异常的3种方式 一款很不错的FLASH时种插件 关于c#中委托使用小结 WEB网站常见受攻击方式及解决办法 判断URL是否存在 提升高并发量服务器性能解决思路
C#不用union,而是有更好的方式实现 用过C/C++的人都知道有个union,特别好用,似乎char数组到short,int,float等的转换无所不能,也确实是能,并且用起来十分方便.那C# ...
- 【译】更快的方式实现PHP数组去重
原文:Faster Alternative to PHP’s Array Unique Function 概述 使用PHP的array_unique()函数允许你传递一个数组,然后移除重复的值,返回一 ...
- HashMap两种遍历数据的方式
HashMap的遍历有两种方式,一种是entrySet的方式,另外一种是keySet的方式. 第一种利用entrySet的方式: Map map = new HashMap(); Iterator i ...
- Android的按钮单击事件及监听器四种常见的实现方式
第一种:匿名内部类作为事件监听器类<ignore_js_op>大部分时候,事件处理器都没有什么利用价值(可利用代码通常都被抽象成了业务逻辑方法),因此大部分事件监听器只是临时使用一次,所以 ...
- 浅淡Webservice、WSDL三种服务访问的方式(附案例)
Webservice Webservice是使应用程序以与平台和编程语言无关的方式进行相互通信技术. eg:站点提供访问的数据接口:新浪微博.淘宝. 官方解释:它是一种构建应用程序的普遍模型,可以在任 ...
- 更快的方式实现PHP数组去重(转)
概述 使用PHP的array_unique()函数允许你传递一个数组,然后移除重复的值,返回一个拥有唯一值的数组.这个函数大多数情况下都能工作得很好.但是,如果你尝试在一个大的数组里使用array_u ...
随机推荐
- 实战weblogic集群之创建节点和集群
一.启动weblogic,访问控制台 weblogic的domain创建完成后,接下来就可以启动它,步骤如下: $ cd /app/sinova/domains/base_domain/bin $ . ...
- java编码问题深入总结
Java语言能够这么普遍的应用,与其国际化的能力是 分不开的,国际化的编码是Java国际化中最重要的一个组成部分,Java的国际化编码能力与其使用Unicode编码是直接相关的.在Java中,任何 ...
- USACO月赛数据
终于找到了usaco月赛的数据…… 根据月赛的名称,我们可以写出数据地址.比如08年一月的月赛即是:http://contest.usaco.org/JAN08 这里要注意区分大小写.
- poj2245
题目分析: 抽奖 在德国乐透你需要从49个数(1,2,3......49)里面选择6个数,玩德国乐透一个流行的策略-尽管他不会增加你获奖的机会-选择一个子集S包含k(k>6)个数从这49个数字里 ...
- Solr系列一:Solr与Tomcat的整合
第一次尝试着去写一个系列的教程,希望自己能坚持下去,也希望自己能够通过博客的编写来加深自己对solr搜索的理解. Solr与Tomcat的整合网上有很多教程,我就以我的整合为例来讲述一下我的整合过程, ...
- Android中使用HttpGet和HttpPost访问HTTP资源
需求:用户登录(name:用户名,pwd:密码) (一)HttpGet :doGet()方法//doGet():将参数的键值对附加在url后面来传递 public String getResultFo ...
- POJ 1330 Nearest Common Ancestors(LCA模板)
给定一棵树求任意两个节点的公共祖先 tarjan离线求LCA思想是,先把所有的查询保存起来,然后dfs一遍树的时候在判断.如果当前节点是要求的两个节点当中的一个,那么再判断另外一个是否已经访问过,如果 ...
- android应用的不同版本间兼容性处理
在Android系统中向下兼容性比较差,但是一个应用APP经过处理还是可以在各个版本间运行的.向下兼容性不好,不同版本的系统其API版本也不同,自然有些接口也不同,新的平台不能使用旧的API,旧的平台 ...
- 2015 Multi-University Training Contest 1 题解 BY FZUw
题目链接:5288-5299 HDU5288 题解原文链接:我是链接
- bootstrap datetimepicker 时间段选择限制
<!DOCTYPE html> <html> <head> <title></title> <link href="./bo ...