代码风格.docx
- 文档编号:16555866
- 上传时间:2023-07-14
- 格式:DOCX
- 页数:27
- 大小:29.58KB
代码风格.docx
《代码风格.docx》由会员分享,可在线阅读,更多相关《代码风格.docx(27页珍藏版)》请在冰点文库上搜索。
代码风格
代码风格
代码风格
代码风格最早出现的,也较为传统的是K&R风格。
所谓K&R即指《TheCProgrammingLanguage》一书的作者Kernighan和Ritchie二人,这是世界上第一本介绍C语言的书,而K&R风格即指他们在该书中书写代码所使用的风格。
K&R风格在处理大括号时,使用了一种较为紧凑的格式,将左括号留在前一行的末尾,并尽可能地压缩·..
目录1基本的代码风格
2代码风格的必要性
3换行的讲究
代码示例1-1:
寻找最佳的断行位置
代码示例1-2:
代码示例1-3:
代码示例1-4:
代码示例1-5:
每行只写一条语句
分行定义变量
4避免代码过于拥挤
使用空行分隔代码块
使用空格降低代码密度
5符号两侧添加空格的基本规则:
单目运算符
在双目、三目运算符
括号
不要使用连续的两个或多个空格
6如何缩进
嵌套或包含关系引起的缩进
因换行而产生的缩进
使用空格还是Tab键
7大括号
大括号的位置
空的大括号结构
仅包含单个语句的结构体1基本的代码风格编辑
假设我们写的是文章而不是程序,那么你一定觉得诸如文章应该分为若干个自然段、每段开头空两格之类的规则是理所当然的。
如果段落的开头不空两格,或者干脆把整个文章写成单独的一段,仔细想来似乎也不会影响文章实质内容的表达。
既然如此,我们为什么还要在形式上下功夫呢?
设想一下,如果你手中的这本书既无章节也无目录,正文中的不同内容都使用同样的字体字号印刷,几百页纸从头至尾洋洋洒洒如念经般地“一气呵成”,你还有耐心看下去吗?
这是一个人人都能理解的道理,可是当文章变成程序的时候,就不是每个人都能想得通的了。
不仅仅是初学者,甚至一些熟练的开发人员,也会写出凌乱不堪的代码。
许多人一定有过这样的经历:
一年半载之后,自己原来写的程序就完全看不懂了。
如果这段程序只是为了交作业,或者临时一用,那还可以不去追究,但如果这是一个商业软件,现在需要根据客户的要求进行修改的话,工作量可就大了——你不得不先花时间把你原来的思路看懂。
肯定会有人反驳:
代码是给机器运行的,又不是给人看的,写那么好看有什么用?
他的话只对了前半句:
代码确实是给机器运行的,可是机器总共才需要看它几分钟?
你花一个月编写的程序,机器顶多两三分钟就编译好了——在这两三分钟之前,这代码不都是你在看吗?
开发软件编写代码不是一朝一夕的事情,更多的情况下,一个软件的开发要经历很长的时间,并且常常由多人合作完成。
一个庞大的软件项目,可能会动用上千名程序员工作数年!
如果把代码写得连自己都看不明白,怎么与别人交流?
同一个开发团队内,一定要保持良好且一致的代码风格,才能最大化地提高开发效率。
2代码风格的必要性编辑
有的初学者会问:
我现在只是一个人写程序,并不需要和其他人合作,这些条条框框还有什么必要吗?
要知道,团队协作只是一个方面。
我经常遇到这类情况,一些初学者拿着他的程序来说:
“这个怎么不能编译?
”我帮他把代码整理了半天,发现有一个地方丢了半个大括号。
如果他写程序的时候能够稍加注意一些的话,相信此类错误完全可以避免。
保持良好的编程习惯,能够避免的错误还远不止这些。
如果说程序代码中对算法的清晰表述是通过长期训练而获得的,那么本章要介绍的这些方法则无需伤神,你不必对代码做任何实质性的改动,只需要添加一些空行与空格,就可以使其可读性大大提高——这些规则就像写文章应该分段一样简单,只要愿意遵守,那么别人在第一眼看你的代码时,必能感觉到你那良好的编程修养,即所谓“见字如见人”。
3换行的讲究编辑
虽然你完全可以在C#里将所有的代码都连在一行里书写,但想必没有人愿意这么做,谁也不会自己折磨自己的眼睛,何况大多数鼠标对于上下翻页的支持都比左右翻滚好得多。
我相信,这也是大多数人接受将每条语句分行书写的原因,很少有人会怀疑这一点的合理性。
例如下面这行代码,虽然结构很简单,但是它实在太长了,所以被分成了两行:
代码示例1-1:
由于代码过长而进行断行
bitmap=newBitmap(size.Width,size.Height,
System.Drawing.Imaging.PixelFormat.Format32bppArgb);
这一点我相信大家都能理解并愿意遵循,然而问题的焦点并不在于要不要换行,而在于在什么位置换行。
寻找最佳的断行位置
写程序不能像写文章那样,什么时候顶到了边界就换,而必须按照其语法规则,在可以换行的位置断开。
例如,对于包含一个超长表达式的语句来说,我们可以在某两个表达式项之间将其断开,如下所示:
代码示例1-2:
通过断行使代码更加清晰
if(f==ImageFormat.Jpeg.Guid||
f==ImageFormat.Tiff.Guid||
f==ImageFormat.Png.Guid||
f==ImageFormat.Exif.Guid)
{
supportsPropertyItems=true;
}
else
{
supportsPropertyItems=false;
}
原本一个很长的条件表达式,通过在“||”运算符处换行,显得更加的清晰。
有一点需要我们注意的是,当我们进行折行时,要将折行位置处的分隔符(如前一例中的逗号,这一例中的“||”运算符等)留在上一行的行末,给人以“此行并未结束”的直观印象。
这就好像在英文书写中,如果你需要将一个单词拆开,就需要在前一行末尾加上连字符,以表示那个单词并没有结束。
可以看出,换行在防止代码超出屏幕边界的同时,还影响着代码的表达。
因此如何选择合适的换行位置也是很有讲究的。
有的时候,我们并不一定非要在临近右边界的时候才去换行,如果存在更为合理的分法,就应当采用,例如下面的情况:
doublecontainerAspectRatio=(double)container.ClientWidth/
container.ClientHeight;
按理说这样的断行并没有什么问题,它在表达式的两项之间断开,并将运算符留在了上一行的行末。
但是,我相信如果换一种断行方式的话,能够更加清楚地表达出原来的逻辑:
代码示例1-3:
寻找最佳的断行位置
doublecontainerAspectRatio=
(double)container.ClientWidth/container.ClientHeight;
如此一来,这个除法算术表达式就显得较为完整,相比前一种写法而言更能体现其内在的逻辑关系。
通常我们会选择整个表达式中最高的关系层次进行断行,例如上述代码中的“赋值号”和“除号”都是可以考虑的断行点,但相比较而言,除号连接的这个算术表达式只是整个赋值表达式的右半部分,如果在除号处断行,那么不但整个表达式会被截断,连局部的这个除法表达式也会被截断;反之,我们选择在赋值号处换行,可以保持除法表达式的完整,最大限度地减少换行对语句整体结构的破坏。
同样的道理,为了将逻辑体现得更为清晰,我们甚至可以将函数调用中的每一个参数都分行书写,如同下面这样:
代码示例1-4:
将函数调用中的每一个参数都分行书写
RectangleimageBounds=newRectangle(
itemBounds.X+padding,
itemBounds.Y+padding,
itemBounds.Width-padding*2,
itemBounds.Height-padding*2
);
当参数数量较多,参数较长或者包含表达式的时候,这种排版比起单独写成一行更为直观醒目。
对于LINQ查询表达式来说,将每个子句单独写成一行也是好的习惯。
因为这同时符合了T-SQL语言的文化传统。
例如:
代码示例1-5:
将LINQ查询表达式中的每个子句单独写成一行
IEnumerable<int>highScoresQuery=
fromscoreinscores
wherescore>80
orderbyscoredescending
selectscore;
每行只写一条语句
如果说换行是为了防止屏幕左右滚动的话,那么当这个情况不存在的时候,一些人就开始打算充分利用屏幕空间了:
privatestaticvoidSwap(objecta,objectb)
{
objecttemp;
temp=a;a=b;b=temp;
}
看起来好像确实没有占据多少屏幕空间,这只是把三条很短的语句凑在一行了而已——关键的理由是:
它不会引起屏幕的左右滚动。
但是当人们已经习惯于一行一条语句的时候,很可能就会忽视这里有三条语句这个事实(不要指望每次遇到的都像这个例子一样地简单)。
更为重要的一点是,编译器总是按行来进行设计的,将多条语句写在一行会引起不必要的麻烦,例如:
你将永远无法把断点设置在后面的语句上(如图1-1):
图1-1:
一行代码包含多条语句时的断点设置
有的读者会觉得,如果代码复杂,当然应该分开书写,没有必要去节省那点屏幕,但是如果像这个例子中这么简单,写在一起也不会带来什么麻烦。
单纯地看来,他的话不无道理,可是,对于一个开发团队,或者将要进入开发团队的人来说,最重要的是“统一”。
如果我们允许将多条语句合并到同一行代码内,那么怎样的代码才算“简单”到可以合并的程度?
是宽度小于50个字符的可以合并,还是宽度小于51个字符的可以合并?
当一条规定无法被准确地定义的时候,它也就无法执行,从而必将在整个开发团队中产生不一致性,最终导致更多的混乱。
分行定义变量
我们再来看一种情况,这类代码出现的几率更为频繁,它是将相同数据类型的几个变量声明放在了同一条语句中:
intnum,factor,index,length;
如果我说我反对这种写法,一定会有读者大叫起来:
这明明是单独的一条语句,何况C#允许我们在一条语句内声明多个变量,如此一来还可以少写几个“int”,为什么不行?
这种写法,显而易见会给注释带来很大的麻烦。
把它们都写在一起以后,我怎么给每个变量添加注释呢?
如果是分开书写的,那么我可以很容易地为每一个变量添加单独的注释,就像这样:
代码示例1-6:
将每个变量分行定义将有助于单独注释
//要计算的数值
intnum;
//表示影响因子
intfactor;
//元素所在的索引号
intindex;
//数据列表的总长
intlength;
如果觉得这种写法较为繁琐,一定要节约那几个“int”,以强调它们的数据类型相同的话,也可以采取下面的写法:
代码示例1-7:
变量分行定义的折衷方案
intnum,//要计算的数值
factor,//表示影响因子
index,//元素所在的索引号
length;//数据列表的总长
这种方式只使用了一条声明语句,但是每个变量都书写在单独的行上,便于有针对性的注释。
4避免代码过于拥挤编辑
想想人们为什么喜欢为文章添加各级标题以及其他复杂的格式,是因为美观吗?
也许是的,但我相信这些格式可以更容易地让人们理清思路。
可是在程序中,我们无法使用这些手段,所有的代码都是纯文本的,即使VisualStudio的代码高亮功能可以为代码的不同部分标上不同的颜色,但这并不能真正影响到代码本身。
因此,光是换行还是不够的,我们还需要更多的手段。
使用空行分隔代码块
适当地添加空行则是一个非常有效的代码整理方式——有点像文章中的分段,一段意思也许需要若干个句子才能表达清楚,在表达下一段意思之前,应当另起一段。
首先,每个C#代码文件是从命名空间引用开始的,一组引用结束之后,则是命名空间的声明及类型的声明。
很显然地,在命名空间引用与命名空间声明之间,应该留有一个空行以示区隔:
代码示例1-8:
在命名空间引用之后添加空行
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Drawing;
usingSystem.Drawing.Imaging;
usingSystem.IO;
usingSystem.Text;
usingAvilla.Metadata;
usingAvilla.Searching;
//这里用空行隔开
namespaceAvilla
{
//下面的内容省略
一个空行,意味着不同的功能块的分隔,如果读者稍加留心,就会发现VisualStudio自动生成的代码,总是在类型的各个成员之间留有一个空行。
我们在书写代码的时候,也可以模仿这一格式:
代码示例1-9:
在类型的各个成员之间添加空行
///<summary>
///表示一条搜索条件的抽象基类
///</summary>
publicabstractclassSearchCondition
{
///<summary>
///初始化一个<seecref="SearchCondition"/>类型的实例,并指明是否区分大小写
///</summary>
///<paramname="caseSensitive">是否区分大小写</param>
protectedSearchCondition(boolcaseSensitive)
{
this.caseSensitive=caseSensitive;
}
//这里用空行隔开
protectedboolcaseSensitive=false;
///<summary>
///获取或设置一个<seecref="Boolean"/>类型的值,以指示是否区分大小写
///</summary>
publicboolCaseSensitive
{
get{returncaseSensitive;}
set{caseSensitive=value;}
}
//这里用空行隔开
///<summary>
///获取表示此搜索条件的SQL筛选条件表达式
///</summary>
///<returns>
///返回一个字符串形式的条件表达式,可直接用于SQL语言中的WHERE子句
///</returns>
publicabstractstringGetFilterExpression();
}
}
这样排版无疑会使得每个成员的代码段更富独立性,绝大多数的编译器,在自动生成代码时都会遵照此方式排版。
您可能会发现,上例中的caseSensitive字段与CaseSensitive属性之间并未留有空行,这是为了强调字段与其对应用于公开访问的属性之间的联系,关于类似情况,我们将在后面的章节详细讨论。
然而,一个空行意味着的不仅仅是功能模块的界限,它更是对代码逻辑块的划分。
我们无法期望每个操作都只通过一行代码一条语句来完成,大多数情况下,它们都需要许多行代码来执行一个完整的操作。
例如,你想查询数据库,那么你需要先生成SQL代码,建立命令,然后执行这个命令并填充至数据集。
这中间大约需要使用四五行代码,而这四五行代码便组成了一个相对紧密的逻辑块,它与其后面的其他逻辑块即可以通过一个空行来进行分隔。
请看下面的一个例子:
代码示例1-10:
用空行分隔逻辑块
publicstaticstring[]GetPhotoIds(stringfilterExpression,stringsort,boolcaseSensitive)
{
//第一个逻辑段代码根据处理后的参数取得数据行
xml.Photos.CaseSensitive=caseSensitive;
DataRow[]rows=
xml.Photos.Select(filterExpression,sort?
?
string.Empty);
//遍历数据行,取出需要的元素
string[]ids=newstring[rows.Length];
for(inti=0;i<rows.Length;i++)
{
ids=(string)rows["Id"];
}
//返回结果
returnids;
}
这个函数的目的是根据指定的筛选条件和排序规则返回照片的标识号(PhotoIDs),函数内部自然形成了三个逻辑段:
先是根据要求取得原始数据,然后从原始数据中提取我们需要的部分,最后将结果返回。
用空行将这三个逻辑区分隔开来将会更加有利于我们理解其思路。
关于注释的合理使用,我们会在后面的章节中再专门介绍。
既然空行可以起到分隔代码,提高清晰度的作用,那么有的朋友也许会为了强调这种分隔效果,多加几个空行。
可事实的效果是,连续的多个空行,在并未提高多少清晰度的同时,浪费了屏幕的空间,而且会让人觉得前后两个代码段并不相关——事实上它们应该是相继执行的。
空行的意义和文章的段落一样,仅在于表示一个停顿,而并非结束。
使用空格降低代码密度
Basic、Pascal与C这三种早期高级程序设计语言的语法,至今仍在发挥着其重要的作用。
VisualBasic仍然保留着Basic的句法简单、多用完整英文单词、贴
近自然语序的习惯(如And、Not、Inherits、Implements、Handles等等关键字);而Delphi更是延续着Pascal语言那标志性的BEGIN-END作风。
C语言由于在
操作系统开发上取得了成功,使得它在软件开发历史上占据了绝对的优势,相比而言,它的语法更加具有影响力,广泛被C++、Java、C#,乃至用于编写网页
的ECMAScript/JavaScript和Flash的脚本语言ActionScript所吸纳,因此也变化丰富。
但是它那种善用符号的古老特色一直被保留了下来,有理由相信,C语
言是使用符号最多的语言。
当其他语法体系都采用AND、OR等关键字作为运算符时,C语言却使用了“&&”、“||”这样的符号,虽然在语法上并没有增加任
何复杂性,但各种奇形怪状难以记忆的符号还是会令初学者望而却步。
让我们来比较一下下面的几行代码:
BASIC:
Ifa>bAndc<>dOrNote>fThen...
PASCAL:
If(a>b)And(c<>d)Or(Not(e>f))Then...
C:
if(a>b&&c!
=d||!
(e>f))...
这三行的意义是完全相同的,但明显可以让人感觉到清晰程度的差异,Basic和Pascal的代码看上去很容易明白,而C语言的代码却像蚂蚁一般缩成一团。
重要的原因在于:
C语言的运算符几乎都只由“符号”构成,与变量名之间不需要用空格充当分隔符。
这样一来,由于缺少空格的稀释,C语言的代码就像被浓缩过似
的——现如今它除了影响我们阅读以外,没有什么好处。
因此我们有必要人为地添加一点空格,帮它降低代码的“密度”。
这里,我总结了一些关于如何在运算
5符号两侧添加空格的基本规则:
编辑
单目运算符
1.单目运算符(UnaryOperators)与它的操作数之间应紧密相接,不需要空格。
例如:
代码示例1-11:
单目运算符的空格规则示例
y=++x;//++在这里是前缀单目运算,它与x之间无空格
在双目、三目运算符
2.在双目、三目运算符(Binary/TernaryOperators)的左右两侧分别添加空格。
例如:
代码示例1-12:
双目、三目运算符的空格规则示例
inta=3+5;//在双目运算符左右添加空格
intb=a*6+7;
intc=a&b;
intd=b++*c--;//虽然有单目运算符,但双目运算符两侧仍应添加空格
inte=a>0?
1:
0;//在三目运算符左右添加空格
括号
3.括号(包括小括号、中括号与大括号)的内侧应该紧靠操作数或其他运算符,不需要添加额外的空格。
例如:
代码示例1-13:
括号的空格规则示例
intf=(a+b)*c;//括号内侧紧靠操作数,因其他运算符添加的空格留在外侧
intg[MAX]={1,2,3};//中括号与表达式中的大括号也同样处理
不要使用连续的两个或多个空格
4.不要使用连续的两个或多个空格。
其实,如果理解了这些规则,在实际书写的时候很容易遵循。
对于任何一个表达式,我们先把单目运算符和括号去掉,然后在双目、三目运算符的左右两侧分别
添加一个空格,再将单目运算符和括号填回去,放在靠近自己操作数的一边即可。
关于函数调用时,要不要在函数名和其后的括号之间添加空格的问题已经讨论了很久。
其实这个是一个无伤大雅的事情,无论使用何种方式,都不会对代码
的可读性产生多少实质性的影响,纯粹是各人喜好罢了。
不过在这里,我建议采用VisualStudio中的默认规则:
在函数调用时不添加空格,而在一些类似的带括号的语法结构中添加空格。
请看下面这段代码:
代码示例1-14:
函数调用时的空格规则示例
stringcmd=string.Empty;
//函数形式的调用,括号前没有空格
cmd=Console.ReadLine();
//语句结构,括号前有空格
if(cmd.Length>0)
{
Console.WriteLine(cmd.ToUpper());
}
else
{
Console.WriteLine("(Empty)");
}
这段代码中的ReadLine、WriteLine都是函数调用,因此与其后面的括号紧密相连,不需要添加空格。
而if结构虽然同样带有类似的括号结构,但是它属于C#的内部语法,为了以示区别,在if与括号之间添加了一个空格。
除if外,switch、for、while等都应做同样的处理。
6如何缩进编辑
在有关代码风格的问题中,最为显眼的可以说就是代码的缩进(Indent)了。
所谓缩进,是通过在每一行的代码左端空出一部分长度,更加清晰地从外观上体现出程序的层次结构。
为了更好地描述这一点,先请读者欣赏下列这段代码:
intkmp_match(char[]t,char[]p,int[]flink,intn,intm)
{
inti=0,j=0;
while(i<n)
{
while(j!
=-1&&p[j]!
=t)
{
j=flink[j];
}
if(j==m-1)
{
returni-m+1;
}
i++;
j++;
}
return-1;
}
我想,就算让你检查一下它里面有没有大括号配对错误恐怕都很困难,更不用说这段代码有什么功能了——你能一眼看清楚每个while循环的内容是什么吗?
让我们换个方式,看看另一段程序:
代码示例1-15:
正确缩进的例子
intkmp_match(char[]t,char[]p,int[]flink,intn,intm)
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 代码 风格
![提示](https://static.bingdoc.com/images/bang_tan.gif)