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. 数据结构(线段树):CodeForces 145E Lucky Queries

    E. Lucky Queries time limit per test 3 seconds memory limit per test 256 megabytes input standard in ...

  2. sharepoint2007的中文版

    今天终于下载到了sharepoint2007的中文版了,以前微软的测试板也没有中文版,今天终于下载到了,经过测试,用英文版的sn也是可以用的.微软提供的测试版,输入正式的key,就可以变成正式版了.我 ...

  3. QQ空间自动发广告解决方法

    最近空间好多人QQ都中了毒.每天我都有几十个好友刷空间话费.流量广告! QQ空间自动发广告的原因: 最近使用了刷赞或者其他QQ外挂软件(有些开发者或破解者会在这样的软件上留后门,请自己判断). 或者最 ...

  4. String拼接也有用加号更好的时候

    做String拼接时用StringBuilder(或StringBuffer)好还是直接用+号性能好?一般来说是前者,不过也有用加号略好的时候.首先我一直认为用+号有很好的可读性,而且当String拼 ...

  5. [Locked] Paint House I & II

    Paint House There are a row of n houses, each house can be painted with one of the three colors: red ...

  6. loadView是干什么用的

    文/natewang(简书作者)原文链接:http://www.jianshu.com/p/f8d261d49615著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. viewContro ...

  7. iOS 开发 入门:使用Ad Hoc 进行用户测试

    在完成iOS开发,准备进行发布之前,我们都希望App能在周围的朋友之间先进行测 试,提提意见,修改完善之后再发布到App Store上.Apple考虑到这一点,因此通过Ad Hoc来实现发布前的用户测 ...

  8. qt 关于内存泄漏的检测

    Qt 关于内存泄露的检测: 收藏人:guitarhua     2012-02-10 | 阅:  转:    |   来源   |  分享               Qt 关于内存泄露的检测:工具篇 ...

  9. Android(java)学习笔记244:多媒体之SurfaceView

    1. SurfaceView:     完成单位时间内界面的快速切换(游戏界面流畅感). 我们之前知道一般的View,只能在主线程里面显示,主线程中更新UI.但是SurfaceView可以在子线程中里 ...

  10. Audio Capture 录音

    The Android multimedia framework includes support for capturing and encoding a variety of common aud ...