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代码段的更多相关文章

  1. 少年,是时候换种更优雅的方式部署你的php代码了

    让我们来回忆下上次你是怎么发布你的代码的: 1. 先把线上的代码用ftp备份下来 2. 上传修改了的文件 3. 测试一下功能是否正常 4. 网站500了,赶紧用备份替换回去 5. 替换错了/替换漏了 ...

  2. Java代码消除switch/case,if/else语句的几种实现方式

    转自:https://my.oschina.net/stefanzhlg/blog/372413 我们在平时的编码中,我们经常会遇到这样的情况: 使用过多的switch/case 或者 if else ...

  3. angularjs中向html页面添加内容节点元素代码段的两种方法

    第一种方式:原生JS向html页面添加内容节点元素代码段: <!DOCTYPE html> <html> <head> <meta charset=" ...

  4. C#不用union,而是有更好的方式实现 .net自定义错误页面实现 .net自定义错误页面实现升级篇 .net捕捉全局未处理异常的3种方式 一款很不错的FLASH时种插件 关于c#中委托使用小结 WEB网站常见受攻击方式及解决办法 判断URL是否存在 提升高并发量服务器性能解决思路

    C#不用union,而是有更好的方式实现   用过C/C++的人都知道有个union,特别好用,似乎char数组到short,int,float等的转换无所不能,也确实是能,并且用起来十分方便.那C# ...

  5. 【译】更快的方式实现PHP数组去重

    原文:Faster Alternative to PHP’s Array Unique Function 概述 使用PHP的array_unique()函数允许你传递一个数组,然后移除重复的值,返回一 ...

  6. HashMap两种遍历数据的方式

    HashMap的遍历有两种方式,一种是entrySet的方式,另外一种是keySet的方式. 第一种利用entrySet的方式: Map map = new HashMap(); Iterator i ...

  7. Android的按钮单击事件及监听器四种常见的实现方式

    第一种:匿名内部类作为事件监听器类<ignore_js_op>大部分时候,事件处理器都没有什么利用价值(可利用代码通常都被抽象成了业务逻辑方法),因此大部分事件监听器只是临时使用一次,所以 ...

  8. 浅淡Webservice、WSDL三种服务访问的方式(附案例)

    Webservice Webservice是使应用程序以与平台和编程语言无关的方式进行相互通信技术. eg:站点提供访问的数据接口:新浪微博.淘宝. 官方解释:它是一种构建应用程序的普遍模型,可以在任 ...

  9. 更快的方式实现PHP数组去重(转)

    概述 使用PHP的array_unique()函数允许你传递一个数组,然后移除重复的值,返回一个拥有唯一值的数组.这个函数大多数情况下都能工作得很好.但是,如果你尝试在一个大的数组里使用array_u ...

随机推荐

  1. -_-#【jQuery插件】Spinner 数字选择器

    Spinner (jQuery UI)

  2. Android WebView播放视频flash(判断是否安装flash插件)

    Android WebView播放flash(判断是否安装flash插件)  最近帮一个同学做一个项目,断断续续的一些知识点记录一下.一个页面中有一个WebView,用来播放swf,如果系统中未安装f ...

  3. HNOI 2016 省队集训日记

    第一天 DeepDarkFantasy 从东京出发,不久便到一处驿站,写道:日暮里.  ——鲁迅<藤野先生> 定义一个置换的平方为对1~n的序列做两次该置换得到的序列.已知一个置换的平方, ...

  4. 【最小生成树】Codeforces 707B Bakery

    题目链接: http://codeforces.com/problemset/problem/707/B 题目大意: 给你N个点M条无向边,其中有K个面粉站,现在一个人要在不是面粉站的点上开店,问到面 ...

  5. Oulipo - HDU 1686 (KMP模板题)

    题目大意:题目叙述很多,其实只看输入输出也能明白什么意思,给两个串W,T, 判断T串中包含几个串W.   分析:还是基础的KMP应用....................... 直接上代码. === ...

  6. Hdu 3887 Counting Offspring \ Poj 3321 Apple Tree \BZOJ 1103 [POI2007]大都市meg

    这几个题练习DFS序的一些应用. 问题引入: 给定一颗n(n <= 10^5)个节点的有根树,每个节点标有权值,现有如下两种操作: 1.C x y     以节点x的权值修改为y. 2.Q x ...

  7. js点击弹出div层

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  8. [深入React] 1. 开发环境搭建

    React环境其实很简单(不兼容ie8): <!DOCTYPE html> <html> <head> <title>React</title&g ...

  9. [ES6] Array.find()

    Convenient method to find one item in an array, avoid writing and  for + if: let arys = [1,,5,,6] ; ...

  10. [Angular 2] Pipe Purity

    Explaining how Pipes only change by default when your Pipe input parameters change and not when your ...