目录
1 编程风格... 4
1.1 统一编程风格的意义... 4
1.2 变量命名规则... 4
1.3 函数命名规则... 5
1.4 类命名规则... 5
1.5 常见语句书写规则... 6
1.6 注释风格... 7
2 代码优化... 8
3.1 代码优化的意义... 8
3.2 函数内的代码优化... 8
3.3 类内的代码优化... 9
3.4 类之间的代码优化... 10
4 调试技巧... 11
4.1 编译时的错误... 11
4.2 运行时的错误... 11
4.3 C#常见问题... 11
1 编程风格
1.1 统一编程风格的意义
- 增加开发过程代码的强壮性、可读性、易维护性
- 减少开发人员编程所需的脑力工作
- 为软件的良好维护性打下好的基础
- 在项目范围内统一代码风格
- 通过人为以及自动的方式对最终软件应用质量标准
- 使新的开发人员快速适应项目氛围
- 支持项目资源的复用:允许开发人员从一个项目区域(或子项目团队)移动到另一个,而不需要重新适应新的子项目团队的氛围
- 一个优秀而且职业化的开发团队所必需的素质
1.2 变量命名规则
· 前缀(小写字母加下划线)表明变量的作用域,无前缀则表明是局部变量或函数的参数。如:
§ m_xx 表示是类的成员变量,控件变量例外
§ g_xx 表示是全局变量,在C#中,也可以理解为在整个项目中都可能用到的静态变量
§ XX 表示是一个常量,用大写,如果多个单词则用“_”格开
· 前缀用数据类型全称中的关键字母代表特定的数据类型(一个或多个小写字母),如下表。
常用数据类型缩写 |
数据类型 |
i |
int |
b |
bool |
str |
string |
c |
char |
f |
float |
d |
double |
ob |
object |
lbl |
Label |
txt |
TextBox |
btn |
Button |
cmb |
ComboBox |
mnu |
Mainmenu |
mnuItem |
MenuItem |
chk |
CheckBox |
grd |
DataGrid |
tm |
Timer |
frm |
Form |
pnl |
Panel |
gup |
GroupBox |
tv |
TreeView |
rdo |
RadioButton |
lb |
ListBox |
tlb |
ToolBar |
dt |
DateTime |
cn |
Connection |
cmd |
Command |
ds |
DataSet |
da |
DataAdapter |
dv |
DataView |
dbTable |
DataTable |
dbReader |
DataReader |
param |
Parameter |
dbRow |
DataRow |
dbCol |
DataColumn |
注:如果模块中只有一个类实例对象,则可以只用简写。如Connection对象可以用conn来命名。还有个别控件很难用缩写,就用它的全称
- l 用匈牙利命名法命名, 如: bool m_bFlag;
1.3 函数命名规则
· 函数名用首字母大写的英文单词组合表示(如用动词+名词的方法),其中至少有一个动词
· 应该避免的命名方式
§ 和继承来的函数名一样。即使函数的参数不一样,也尽量不要这么做,除非想要重载它
§ 只由一个动词组成,如:Save、Update。改成如:SaveValue、UpdateDataSet则比较好(建议使用)
· 函数参数的命名规则
§ 函数参数应该具有自我描述性,应该能够做到见其名而知其意
§ 用匈牙利命名法命名
1.4 类命名规则
· 类的命名通常以父类的名称结尾,例如:
Class PictureButton:Button
但是窗体类例外。如:FrmXXX可看出该类从Form中继承而来
· 类名中尽量不要出现下划线
· 类变量的命名可以参照,如:FrmXXX frmXXX = new FrmXXX(),即首字母小写即可
1.5 常见语句书写规则
如下表所示。
语句 |
提倡的风格 |
if |
if(condition) { statements; } else { statements; } |
for |
for(initialization; condition; update) { statements; } |
foreach |
foreach(something in collection) { statements; } |
switch |
switch(…) { case ..: break; case …: break; default: } |
while |
while(..) { statements; } |
do-while |
do { statements; } while(condition); |
try-catch |
try { statements; } catch(Exception e) { handle exception; } |
同一代码块内的不同逻辑块之间应空一行 |
{ do statement1;
do statement2; } |
函数与函数之间至少空一行 |
|
1.6 注释风格
· 注释应该正确、简洁、有重点
· 应该写优雅的、可读性良好的代码,而不是为玄妙、晦涩的代码写注释
· 原则上应尽量减少程序体内代码的注释,应该保持代码本身的直接可读性
· 文件头要的注释
- 文件标识号:就是该文件的文件名
- 修改历史
- 设计追溯:该文件的内容的设计是根据需求规格说明书(非多媒体项目则为详细设计说明书)的哪一部分设计的
- 例:
#region 文件说明
/// 1.文件标识号
/// FrmMain.cs
/// 2.修改历史
/// 2005-12-10 张三 V1.1 修改(界面风格修改,控制功能移至flash中实现)
/// 2005-11-25 张三 V1.1 修改(添加进度条控制flash)
/// 2005-11-11 李四 V1.0 修改 (添加树控件)
/// 2005-11-5 张三 V1.0 初建(已进行单元测试)
/// 3.设计追溯
/// ML_ITOD_PROC_DS_302(需求规格说明书) 3.1
- #endregion
· 函数的注释,可以只对public或者重要的private函数进行注解
· 相应功能块应用#Region…#Endregion包含起来
例1:
#Region 功能1
void fun1()
void fun2()
#endregion
#Region 功能2
void fun3()
void fun4()
#endregion
例2:
void fun1()
{
#region 功能1
…………
#endregion
#region 功能1
…………
#endregion
}
· 对类、函数、和类变量采用描述性注释(“///”)
- 函数要特别注意用以下格式注释
项目 |
说明 |
<summary> |
简介 |
<param> |
参数说明 |
<returns> |
返回值 |
<remarks> |
注释 |
- 例:
/// <summary>
/// 这是一个例子的类
/// </summary>
public class TestClass
{
/// <summary>
/// 这是一个类变量,用这种注释的话可在类的任何位置看到它的注解
/// </summary>
private bool m_bReCall;
/// <summary>
/// 函数的注释就是这样,函数的功能,但尽量做到顾名思义
/// 函数内的代码也一样,最好代码能一目了然,尽量避免在函数中写大量注释
/// </summary>
/// <param name="sender">参数的说明</param>
///<Retrun>返回值的说明</ Retrun >
private int InitializeComponent(Object sender)
{
}
- }
2 代码优化
2.1 代码优化的意义
· 仅仅对符合功能说明书的要求、能正确运行的代码进行优化是有意义的
· 代码优化能减少冗余代码的数量,用更少的代码来实现同样的功能
· 提高代码的内聚程度,减少耦合程度
· 对代码的抽象能提高代码的重用度,对今后其他项目的进度有非常重要的意义
2.2 函数内的代码优化
· 去掉从来没有用到过的参数
· 始终进行参数检验。不要认为只有我才会调用这个函数,我能够保证参数的有效性。事实上很多运行错误就是没有对参数进行检验。对于传入了非法值的函数调用,可以返回一个对调用无意义的值(如:null,-1),或者干脆抛出一个异常
· 函数的参数不宜过多,如果实在是太多,可以考虑将这些参数封装在一个类中,然后将这个类的某个实例作为参数传入函数
· 如果函数中用到的类成员变量或者其他全局变量可以用传入参数的方式代替,则用参数代替,这样可以减少该函数和外界的关系,提高内聚
· 一个单一的函数的代码量不宜过多。如果实在很多,则可以把它切分成小的函数,例如长的switch语句是最容易切分的
· 单个函数中尽量避免相同的代码,可以用条件语句或者抽取出来作为函数的方法消除这些冗余
2.3 类内的代码优化
· 只有类对外的接口才声明为public
· 在类的成员函数中如果存在着相同的代码,则将其抽取成为private的成员函数,以减少代码的冗余,保持在一个类中没有相同的两份代码的副本
· 尽量减少成员函数之间的依赖,特别是对成员变量值的依赖
2.4 类之间的代码优化
· 类应该是一个实体,具有自己的数据和对这些数据的操作
· 把界面操作和数据处理分离在两个类中是比较好的做法
· 对于不同类之间有相同代码的情况,有以下几种处理方法:
§ 将相同的代码抽象出来作为父类,其他的类从中继承,由此来共享代码
§ 将相同的代码抽象出来作为一个新类,其他类中声明一个该类的变量,由此来共享代码
- 这两种方法各有利弊,前种方法比较适于当共享代码在调用之前必须做特殊的初始化,而这些初始化可能很难用函数调用来完成,这时父类的初始化代码中可以加入一个虚拟函数,所有的子类都重载该函数,做特定的初始化;后种方法可以封装得很彻底,只暴露出对外的接口,和其他类的耦合程度比较小
· 任何重复的代码都可以抽取出来,不仅仅是对数据进行处理的代码,界面代码同样可以抽取出来
· 如果许多类都有做类似事情的函数,名称相同、内部具体的操作不同,这时候可以将这些函数提取出来作为一个接口。其他类都从中继承,然后根据自己的要求来实现之
2.5 禁止过度依赖异常处理
不要将它们用作控制正常程序流程的方式,如果有可能检测到代码中可能导致异常的状态,就不要在处理该状态之前捕获异常本身。如检测除数为零的情况应如下:
if (num != 0)
result = 100 / num;
else
result = 0;
而不应该使用异常处理如下编写:
try
{
result = 100 / num;
}
catch (Exception e)
{
result = 0;
}
3 调试技巧
3.1 编译时的错误
· 始终在“输出”窗口中看程序编译的输出,“任务列表”窗口中经常会遗留以前编译后留下来的消息
· 认真查看编译输出的错误消息,掌握正确的错误地点和信息
· 当碰到莫名其妙的编译时的错误应
1) 重新编译整个项目或者解决方案。
2) 关闭Visual Studio.NET,然后再打开。
3) 重新启动计算机。
4) 保证编译出来的程序不在运行中或者所有的输出文件的属性都是可写的。
3.2 运行时的错误
· 首先要读取异常信息,猜测大概的发生地和发生原因
· 仔细读发生异常处源代码
· 在相应处设置断点,然后单步运行
· 如果还是找不出错误,可以请同事帮忙。当着同事的面讲解自己的源代码,旁观者看得最清
· 配置问题和数据库中数据的错误也会导致运行时的错误
3.3 C#常见问题
· C#中控件的消息(事件)处理是立即的。也就是说,如果对某个控件的某个消息写了消息处理函数,然后假如当程序中某处的代码A引发了该消息时,程序流程会立即跳转到该消息的消息函数中去,如果这时消息函数中发生异常,即使代码A处于异常块中,该异常也无法捕获。所以如果出现在给控件的某个属性赋值后发生异常的情况,则请找一下是否已经对该控件的该属性写了消息函数(别忘了在父类也许会有),如果有的话,则应在这个消息处理函数中也加上断点
· 注意集成环境中窗体设计器的副作用。对于处在InitializeComponent中的代码,如果需要做修改,尽量先将其搬到函数外面来,否则,不能保证修改过的代码不被集成环境改回来或者删掉
· C#中很多异常都是由于强制转换产生的,所以对强制转换一定要放在异常处理块中
private void button1_Click(object sender, System.EventArgs e)
{
// 这是一种转换的方法,不太推荐
try
{
Button btn = (Button)sender;
}
catch
{
}
// 这是一种转换的方法,推荐
Button btn2 = sender as Button;
if (btn2 != null)
{
// todo
}
}