您的位置:首页 > 其它

我的公司培训讲义(1):.NET开发规范教程

2013-08-14 20:19 204 查看

这是1年多以前我在公司所做讲座的讲义,现在与园友们分享,欢迎拿去使用、一起讨论。文中有若干思考题,对园友们是小菜一碟。另有设计模式讲义一篇,随后发布。
博文上了首页,感谢博客园团队推荐,也感谢所有园友的支持。
经园友提醒,我们推荐新手使用ReSharper插件,它可以自动提示不符合代码规范的地方。


1概述

1.1意义

“.NET平台开发规范”包括两方面内容:代码规范、最佳实践。

(1)规范和优美的代码风格有助于思路清晰。

(2)规范和优美的代码有助于团队沟通交流。

(3)规范和优美的代码有助于学习近20年(后设计模式时代)现代面向对象思想发展的精华。

.NET平台是继Java平台之后又一伟大的带集大成面向对象类库的虚拟机平台。.NET平台及其主力语言C#吸取了Java平台及Java语言的全部优点和精华(包括开发规范),并逐渐发展和独创出了鲜明的、先进的特色。

众所周知,C#最初全盘照搬了Java。但时至今日,Java语言准备采用C#中的lambda语法。Java平台是个优秀的平台(类库、虚拟机),但Java语言却日益获得差评,于是JVM上的其他语言越来越多地取代了Java,如Groovy,Scala,JRuby等。

.NET平台上的语言数量比Java平台有过之而无不及,但与之截然不同的是,C#始终在.NET中保持绝对优势地位。C#也许不是.NET上最优秀的语言,但已经足够优秀来进行生产。而Java语言却称不上足够优秀,因为有语言专门用来弥补它的不足,最典型的就是Scala。而Scala解决的问题,如函数式编程、并发编程,在C#中早有解决方案。

毫不夸张地说,.NET是现代面向对象开发思想和实践的一个制高点,而C#则是编程语言发展趋势(声明性、动态性、并发性)的风向标。

即使你是一名C++开发人员,你也能从中获益。

(4)规范是微软内部秘笈,优美是秘笈指导下的修炼。

这些规范和最佳实践多来源于微软负责CLR、BCL、C#/VB团队的成员。它们体现了.NET和C#设计人员的智慧结晶。例如,他们会告诉你哪些功能已经被哪些新功能取代而不建议使用,而新功能新方法总是以优雅的方式解决旧功能存在的问题,会给人启发,很值得回味,使用新功能收获的不仅是方便快捷,还有水平层次的提升。

1.2后设计模式时代大事记

Python/Ruby等动态面向对象语言的兴起

Java的诞生

.NET的诞生

Boost库的发展

函数式编程融入面向对象语言

其他门派面向对象语言JavaScript和Objective-C的繁荣

新C++标准

1.3参考资料和延伸阅读

本教程主要综合以下资料中的观点和我公司开发实践写成:

《C#编程风格》(TheElementsofC#Style)

《.NET设计规范:约定、惯用法与模式(第二版)》(FrameworkDesignGuidelines:Conventions,Idioms,andPatternsforReusable.NETLibraries,2ndEdition)

EffectiveC#:50SpecificWaystoImproveYourC#,1stand2ndEdition

MoreEffectiveC#:50SpecificWaystoImproveYourC#



2代码规范

2.1空白

用空行分隔逻辑块(函数、条件块、循环块等)

运算符空格(使用IDE自动设置)

命名空间导入,在不同公司或不同库的命名空间之间空一行。

2.2代码块

切分长语句为多行语句

左花括号也要单独成行,与右花括号在同一铅垂线上,与上一级花括号缩进一个制表位。

始终使用语句块



if(a==null)

{

return;

}

if(a==null)return;

使用#region标记代码区域,便于折叠

2.3命名

使用有意义的名称,如IsDepartmentReadyToOfferJob

使用完整单词,帕斯卡(Pascal)和骆驼(Camel)写法



NumberOfArgumentsIn

nargin

根据含义而非类型命名



ParcelName

strParcelName

不要用大小写区分名称

像普通词一样书写缩略词:XmlReader;System.Linq

用复数形式书写集合名称

习惯性后缀(前缀):

抽象基类

-Base

接口

I-

异常

-Exception

事件参数

-EventArgs

事件委托

-EventHandler

特性

-Attribute

泛型类型参数

T-

给实现一种设计模式的类加上模式名称,例如:工厂模式-xxxFactory

泛型参数的命名:使用单个大写字母,如T,U等。需要时,写成TKey,TValue等

枚举的命名:普通枚举用单数名词,位域枚举用复数名词。

接口的命名:使用I-加名词为声明服务的接口命名,使用I-加形容词为描述能力的接口命名。(例:IEnumerable和IEnumerator)

[思考题1]

从右边找出与左边项目相关的接口。

资源回收


a

IFormattable

Convert.ToString()


b

IEquatable

重载关系运算符


c

IConvertible

if(textbox1.Text=="Hello")


d

IClonable

Double.ToString()


e

IDisposible

foreach循环


f

IComparable

frompinqselectp;


g

IEnumerable

复制数组元素


h

IQueryable

用能体现布尔值特征的名称给布尔类型命名:IsXxx,HasXxx,CanXxx

方法的命名:用动[宾]结构、帕斯卡写法

变量和方法参数命名:用名词性结构、骆驼写法

成组的私有字段、公共属性和构造函数形参:_item,Item,item。见3.6节代码示例

习惯中允许使用的标准“一次性”变量名(只限这几个):

i,j,k

循环变量(类型:System.Int32)

o

System.Object

s

System.String

e

事件实参(基类:System.EventArgs)

ex

异常(基类:System.Exception)

g

System.Drawing.Graphics

x,y,z

Lambda表达式的形参

命名空间的命名:公司名.产品名.技术/模块名。将相互依赖的类型放在同一命名空间下。不要污染框架命名空间(例如在System命名空间中添加类型)。

2.4注释

使用内建XML注释文档机制

内部注释:尽量少用,避免行末注释

不用C风格注释块/**/

在右花括号、else后面加注释辅助阅读

1

2

if(a>0)

{

}

else//a<=0

{

}

}//endofforeach

2.5字面量

避免使用数值字面量。使用常量表示一个值,使用枚举明确指示一个状态。

使用枚举代替布尔值。

使用标准形式书写浮点字面量。不必写f,d(一般只用double),但要对整数写上小数点。用科学计数法时注意小数点前只能有一位数,且不能是0。

字符串字面量应该总是赋给变量使用。用string.Empty代替空串字面量。

2.6其他

不要依赖运算符优先级。应当加上括号。

单独声明每个变量,不要写inta,b。

给switch语句加上default

for循环变量声明在循环作用域内部,foreach循环不要再声明循环变量。或用框架函数代替循环,它们的思想是一致的,见最佳实践。

3最佳实践

3.1使用属性

属性是函数成员,没有存储空间。例:属性不能做ref/out参数。

将成员字段全部私有,使用属性来获取,不需修改就不设set。

属性应简单、低成本、无副作用、与调用顺序无关。

属性返回集合应该总是只读。(用户代码可以清空集合、再添加元素,但不允许使对象引用改变)

属性返回其他引用类型也应该尽量考虑只读,用户代码依然可以修改成员。

特殊的属性:索引器

[思考题2]

strings="abcdeeg";
s[5]='f';


以上代码在C++和C#中是否都合法?是否都有效?

3.2使用不可变值类型

为“值”语义使用struct。所谓值语义就是一种状态、属性,例如点(坐标)、颜色(分量)等,多具有短小数据量的特点。

为struct的字段添加readonly修饰符,成为不可变(Immutable)类型。成员函数返回新实例,而不是修改this实例。

值类型的成员字段只应使用基本CTS类型。

3.3转换

用as和is运算符代替显式引用转换

基本类型的转换要使用System.Convert类

避免装箱和拆箱

3.4字符串

[思考题3]检测一个字符串str是否以"abc"开头,有两种方法(不使用StartsWith):

str.SubString(0,3)=="abc";
str.IndexOf("abc")==0;


请问哪种方法好,为什么?

CLR中的字符串是不可变类型,字符串分配是一项昂贵的操作。因此我们有以下最佳做法:

用string.Empty代替空串字面量。

用str.Length==0或string.IsNullOrEmpty(str)检测空串,后者还兼容空引用。

反复使用的字符串应当创建变量多次引用而不是每次都使用字面量(每出现一次字面量就要重新分配一次字符串)。

不分大小写的比较应当用string.Compare(str1,str2,true)而不是str1.ToUpper()==str2.ToUpper()。

考虑使用string.Format()方法而不是字符串拼接来输出结果。

大量拼接字符串要使用StringBuilder类。

3.5使用查询表达式代替循环

函数式编程为C#带来了高可读性的声明式语法。循环是一种命令式语法,在声明式语法中相应的是对集合的mapping操作。

以下代码生成前100个完全平方数:

varsquares=Enumerable.Range(1,100).Select(x=>x*x);


返回int数组:squares.ToArray();

返回List<int>集合:squares.ToList();

以下代码对集合foos中每个元素执行DoSomething()成员方法:

foos.ForEach(x=>x.DoSomthing());


以下代码选出foos中大于100的元素:

varq=foos.Where(x=>x>100);


points中点按到原点距离排序:

varq=points.OrderBy(x=>x.DistTo(newPoint(0,0)));


foos中平方最小的数:

doubleminSq=foos.Min(x=>x*x);
doublemin=foos.Distinct().Single(x=>x*x==minSq);


foos中的数是否有大于100的(是否都大于100)

foos.Any(x=>x>100);
foos.All(x=>x>100);


3.6返回多个值

在Matlab中可以用矩阵返回多个值,这就是元组(Tuple)的概念。当函数需要返回多个值时,应当使用元组而不是使用输出参数。.NET4.0中提供了Tuple泛型类,位于System命名空间。在3.5以下版本可以考虑自己实现。

publicclassTuple<T1,T2>
{
privateT1_item1;
privateT2_item2;

publicT1Item1{get{return_item1;}}
publicT2Item2{get{return_item2;}}

publicTuple(T1item1,T2item2)
{
_item1=item1;
_item2=item2;
}
}

publicclassTupleList<T1,T2>:List<Tuple<T1,T2>>
{
publicvoidAdd(T1item1,T2item2)
{
base.Add(newTuple<T1,T2>(item1,item2));
}
}


在任何时候都应避免使用ref/out传递参数,尤其对引用类型(禁止引用的引用)。尝试改进你的设计。

3.7对象初始化

Buttonbtn=newButton{Text="Clickme!",BackColor=Color.FromArgb(255,255,255)};
btn.Click+=(sender,e)=>MessageBox.Show("Hello!");


3.8小函数

将大函数分拆成小函数。这样做的好处有:

合并重复代码,便于维护

增加函数层级,便于调试

方法参数也不宜过多,否则也应考虑拆分。

3.9using块和finally

以下代码编译成相同IL:

//1.ExampleUsingclause:
using(myConnection=newSqlConnection(connString))
{
myConnection.Open();
}

//2.ExampleTry/Catchblock:
try
{
myConnection=newSqlConnection(connString);
myConnection.Open();
}
finally
{
myConnection.Dispose();
}


3.10使用泛型集合

不要使用非泛型的ArrayList,这会带来不必要的装箱和拆箱。使用System.Collections.Generic命名空间中的容器类型。

代替数组的首选:List<T>。如被用户代码获取应该以只读属性形式提供。如果要真的使集合不可修改,可用ReadOnlyCollection<T>(System.Collections.ObjectModel)

集合做参数禁止ref/out传递(造成引用的引用)。值传递即可达到修改集合元素的目的。

在多个函数间返回、传递的集合考虑用接口类型IEnumerable<T>和yield关键字。连续的LINQ语句考虑不急于调用ToList或ToArray。

其他常用集合:Dictionary<TKey,TValue>;SortedSet<T>(.NET4)

3.11充分利用BCL

BCL中提供了很多常用算法,并且还在不断扩充。例如,.NET4增加了System.Numerics命名空间,目前包括BigIntegar和Complex类,而在bcl.codeplex.com上已经放出了BigRational类的候选版和源代码。

尽可能只依赖BCL,减少第三方依赖。

对字符串的操作都有简单的方法可以调用。用这些方法不仅能更方便省时、稳定高效,而且能使你的代码趋向于可读性强的声明式风格。

尽可能只用系统定义的委托,而不是自己定义委托类型。系统定义的委托包括:各种泛型参数数目的Func,Action;一个Predicate<T>;各种EventHandler。以下是一个高阶函数的应用实例:

Func<Color,EventHandler>changeControlColor=
x=>(sender,e)=>(senderasControl).BackColor=x;
button1.Click+=changeControlColor(Colors.Red);


3.12其他

使用虚函数和多态而不是频繁使用引用转换

创建枚举0值表示未初始化、无效、未指定或默认

总是在派生类构造函数的初始化列表中列出所有基构造函数

总是重写ToString()方法
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
章节导航