Some practices to write better C#/.NET code(译)
C#(.NET)中有关编码的一些建议,原文地址:http://www.codeproject.com/Articles/539179/Some-practices-to-write-better-Csharp-NET-code
目录
开发者之间经常喜欢争论“编码规范”,在整个项目周期中严格遵守编码规范是非常重要的。每个人都应该认识到这种约定是非常有用的,接下来我给大家分享一下这些年来我总结的一些编码规范,有些很基础但是对我们非常有用。
我们先举一个FizzBuzz的例子。现在我们需要写一段代码遍历1到100,如果遍历到的数值是3的倍数,那么输出“Fizz”,如果是5的倍数则输出“Buzz”,如果既是3的倍数也是5的倍数则输出“FizzBuzz”。其余情况下将原数值直接输出。
Examp1:
你觉得上面这段代码怎么样?我们可否写得更好一些呢?
Example2:
现在你觉得这段代码怎么样?我们可否还能写得再好一点呢?
好吧,让我来再将这段代码优化一下。命名这种事是我们工作中最困难的事情之一,因为我们要花很多时间来取名,属性、类、文件以及项目等等。其实我们也应该多花一些时间在命名这件事情上,因为一个好的名称可以具有显而易见的含义,让人一看就懂。
现在觉得怎么样?是不是比之前的例子都好,可读性更强了?
先为人而写代码,其次再为计算机。编写易读的代码比编写复杂难懂的代码用时长不了多少,至少从长远来看。如果你的代码很容易读懂,那么它肯定会工作得很好(译者注:这里指可读性好的代码说明思路清晰,逻辑大部分不会有问题)。这也是要编写易读代码的重要原因。另外,在我们回顾之前写的旧代码时,在别人需要修复一些bug时,在别人需要将你写的一部分代码移植到别的系统中时,也都需要能够读懂我们之前写的源码。
“如果我们只为自己而写代码,为什么还要求编写易读懂的代码呢?”
好吧,如果一个礼拜或者两个礼拜后,你要开发别的系统了(你不再负责原项目了)。那么其他人怎样修复原项目中的bug呢?(过这么长时间后)我想你大概也会完全迷失在你曾经写的糟糕代码之中吧。
我个人觉得,好代码应该包含以下特征:
- 代码容易被修改,容易在原有基础上进行扩展;
- 代码清晰简洁并能够表达清楚含义;
- 代码质量高。
所以,请在满足机器需求的前提下,尽量编写更容易读懂的代码。
首先你得多阅读他人写的代码,然后能够分辨出哪些是好的习惯哪些不是,什么编码风格能让你更容易读懂而什么让你更迷糊,接着将这些好的编码方式应用到自己的代码之中,当然最后你还是需要一些时间、一些经验以及多一些实践来提高你代码的可读性。
对于每个软件公司来说,让员工遵守一个好的编码标准是非常重要的,这可以通过一些培训、员工之间代码相互检查等方式来达到目的。下面介绍几款自动化代码检查工具:
- FxCop
- StyleCop
- JetBrains ReSharper
根据维基百科上的解释:编码规范是指针对某一种编程语言而总结出来的一系列编码指南,旨在约束人们的编码风格、编码实践以及书写每一段代码的方式。这些规范几乎涉及到文件组织、缩进、注释、声明、命名方式、编程原则等等各方面。软件开发者被要求严格遵守这些规则,从而提高源代码的可读性并使软件系统更易维护。编码规范可以以文档的形式分发给每个员工,每个编码人员必须遵守。注意编码规范对于编译器来讲并不是强制性的,因此编码过程中不遵守编码规范并不会影响软件系统的正常运行。
你可能知道类名、方法名、局部变量名以及属性名之间的命名区别,因为它们通常使用不同的大小写规范。你可以在互联网上找到一门编程语言的编码指南,所以找到它并遵守它吧。
下面这些编码指南是微软团队指定的,我仅仅是增加了一些附加内容。
大写规范
下面是C#编程中的一些例子,命名规范以及最佳实践。大家可以参考。
- PascalCasing
标识符中的每个单词的首字母均大写。
- CamelCasing
标识符中的第一个单词首字母小写,其余单词首字母大写。
参见:https://msdn.microsoft.com/en-us/library/ms229043.aspx
一些命名示例
在互联网上你可以找到足够多的资料,我仅仅列举出一部分:
- C# Coding Conventions
- C# Coding Guidelines
- C# Coding Standards and Best Programming Practices
- C# Coding Standards and Naming Conventions
下面是我总结的一些最基本的规范:
1)对于类、方法、属性等,请使用PascalCasing方式
2)对于参数和局部变量,请使用CamelCasing方式
3)不要使用缩写(这里应该是毫无根据的缩写,不能很直观的看出意思,译者注)
4)在标识符中不要使用下划线
5)在接口名称前加I字母
6)类成员变量声明在类的最前面,其中静态成员在最上
7)枚举值使用单一的名称
8)枚举类型名称后不要加Enum后缀
不要定义太大的类
我曾经写过非常大的类,最后证明情况非常糟糕。后来我发现原来我让一个类型负责了太多的工作,这完全违背了单一指责原则,SOLID中的S(译者注:参见之前一篇博客)。
“单一指责原则提倡一个类型只负责单一、相似的功能,这个功能完全包含在该类型内部,并尽可能少的与外界交互。”
或者,依照Martin's 的定义:一个类如果需要修改,那么只应该有且仅有一种合理的原因。
为什么将多个功能拆分开来放在不同的类中非常重要呢?因为每个功能都是一个可变因素,当需求改变了之后,一个负责了太多功能的类需要改变的几率更大。
避免多余的注释
多余的注释指:
“一个注释添加的时间太久、与源代码已经不相关或者完全不正确,那么该注释就多余了。”尽量不要在一个单独的方法或者比较简单的类前面添加注释,因为我见过的大多数这样的注释都只是为了说明方法(类)的实现的功能,很多时候这些注释都是无用的。虽然很多开发者添加注释是为了增加代码的可读性以及后期的可维护性,但是请确保你添加的注释不是在做无用功。如果你给方法定义了一个非常容易理解的名称,那么完全不需要多余的注释去解释它是干什么的。我这样说,是因为很多时候一个好的名字(方法名、类名)比注释高效多了。请看下面的例子:
如果我们将方法定义为CancelExportForDeletedProducts()怎么样呢?这时候就完全不需要多余的注释了。当然了,注释有些时候是不可少的,比如Visual Studio生成一些API文档时,就必须用到注释,你可以使用“///”来为代码添加注释,这样Visual Studio就会自动生成XML文档,为别人提供智能提示功能。
注意,我不是说任何时候都不需要注释。如果你添加的注释只是为了描述代码干什么用的,那么就完全没必要。如果你阅读一些包含大量注释的代码时,就会发现这些代码一般都写得比较糟糕。关于这方面的介绍,可以参见下面几本书:
- Professional Refactoring in C# and ASP.NET by Danijel Arsenovski
- "Refactoring: Improving the Design of Existing Code" by Martin Fowler, Kent Beck, John Brant, William Opdyke, don Roberts
类中避免多余的Region块
Region块是VS提供的一个可以折叠代码的功能,它可以折叠一行或者多行代码。它出现的目的主要是为了在一些比较大的文件中更容易定位到某个具体的方法(代码段),因为它可以隐藏一些非常长的代码片段。但是如果一个类负责了太多功能,那么它就违背了单一指责原则,所以下次在你准备使用Region块来隐藏代码时,请先考虑一下是否应该将这个类拆分成多个类。
方法避免过长
一个方法如果行数过多,那么理解起来就会相当困难。理论上每个方法建议在20~25行代码之间,但是一些黑客(牛逼的人,译者注)喜欢定义1~10行的方法,这个完全取决于个人爱好。方法抽取是代码重构最基本的操作之一,如果你认为一个方法太长或者需要增加额外的注释才能解释它的作用时,那么你就可以考虑抽取方法了。其实代码行数太多并不是问题,问题是当你编写一个行数太多的方法时,你很难跟踪到方法中的每个临时变量。这时候你可以使用Visual Studio抽取方法功能,
- 使用ReSharper
- 使用Microsoft Visual Studio
有关更多的内容,可以参考MSDN。
避免太多的参数
如果一个方法包含太多的参数,那么你可以定义一个类或者结构体将所有参数合并起来。这通常是一个优雅的做法:
然后详细说明我们定义的类(结构体),而非各种参数。
避免过于复杂的表达式
复杂的表达式内部隐藏了复杂的含义,我们可以将复杂的表达式封装到类的属性之中,之后任何地方都使用该属性。这样代码阅读起来更加容易。
将“警告”当作“错误”处理
上面代码中你会发现我们定义了一个变量但是从来没有使用过它。通常我们在编译项目时会发现有很多警告信息,但是项目仍然能够通过编译。这时候我们应该尽可能的处理掉这些警告,你可以将所有警告都当作错误来处理:
尽量减少函数返回点
在方法中尽量减少它的返回点,不然你很难掌握方法会在哪个地方返回。在有些代码分支中,一旦你知道了整个方法执行结果,你可能就会立刻返回结果。
你可以想象到,一个具有20~30行代码的方法中有4个返回点是个什么情况,在方法执行期间,你很难判断它会在什么地方返回。
使用变量时才声明它
许多开发者喜欢在一个方法开始的地方将所有临时变量全部定义完,但是这样是不提倡的。我们更应该在我们需要使用临时变量的地方才开始声明它们,这样可读性更高,因为我们很清楚就可以知道即将使用到的变量初始化的值。
循环结构的检查
通常测试一个循环包含三个关键点:循环开始点、循环结束点以及中间任意选取的一点。如果你写的循环结构还包含其它特殊的情况,那么请细心测试它们。如果循环比较复杂,循环次数较多,那么你可以手动算一下循环次数。
在任何软件公司中,遵守一个编码原则都是非常重要的。所以我还是重复那句话:Find a Convention and stick with it.如果你觉得我漏掉了某些重要的编码规范,可以在评论中给我留言,然后我会更新这篇博客,为快乐而编程:)
(以上翻译有删减)