[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 ...
随机推荐
- 搜索(DLX):HDU 3663 Power Stations
Power Stations Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
- select的使用(一)
单表操作 select Name,Major,InDate from T_Employee as 计算结果 select Name as 姓名,Major,InDate from T_Employee ...
- MFC 显示CImg图片
很多示例关于CImg都是基于控制台的,如何把它用于MFC中显示. Problem:直接按照控制台示例写入MFC程序中,当程序执行完display后,其后面的代码便不再执行. solution:开辟新的 ...
- 自定义WCF的配置文件
原文地址:http://www.cnblogs.com/shanyou/archive/2008/12/02/1346298.html WCF的承载既可以通过编码实现,也能够通过配置实现.而且使用配置 ...
- c++转换构造函数和类型转换函数
看stl源码时,有一段代码感觉很奇怪 iterator begin() { return (link_type)((*node).next); } iterator和link_type是两种不同类型, ...
- VGO新闻 - VGO
VGO新闻 - VGO VGO天津伊势丹店盛装揭幕 VGO天津伊势丹店盛装揭幕2013年9月7日,VGO(微高)全国首家实体店在天津伊势丹百货盛装开幕.现场,100多位商场领导及业内同仁共同出席了
- F - Wormholes
题目大意: 农民约翰在农场散步的时候发现农场有大量的虫洞,这些虫洞是非常特别的因为它们都是单向通道,为了方便现在把约翰的农田划分成N快区域,M条道路,W的虫洞. 约翰是时空旅行的粉丝,他希望这样做,在 ...
- C#快速剔除字符串中不合法的文件名或者文件路径字符
C#快速剔除字符串中不合法的文件名 string strFileName= "文件名称"; StringBuilder rBuilder = new StringBuilder( ...
- MatLab计算图像圆度
本文所述方法可以检测同一图像中的多个圆形(准确的说,应该是闭合图像). 在Matlab2010a中可以实现. 附录效果图: %颗粒圆度 clear;close all; %% %读取源图像 I = i ...
- HDFS Architecture--官方文档
HDFS Architecture Introduction The Hadoop Distributed File System (HDFS) is a distributed file syste ...