目錄
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 統一編程風格的意義
- 增加開發過程代碼的強壯性、可讀性、易維護性
- 減少開發人員編程所需的腦力工作
- 為軟體的良好維護性打下好的基礎
- 在專案範圍內統一代碼風格
- 通過人為以及自動的方式對最終軟體應用品質標準
- 使新的開發人員快速適應項目氛圍
- 支援項目資源的複用:允許開發人員從一個項目地區(或子項目團隊)移動到另一個,而不需要重新適應新的子項目團隊的氛圍
- 一個優秀而且職業化的Team Dev所必需的素質
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
· 函數的注釋,可以只對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處於異常塊中,該異常也無法捕獲。所以如果出現在給控制項的某個屬性賦值後發生異常的情況,則請找一下是否已經對該控制項的該屬性寫了訊息函數(別忘了在父類也許會有),如果有的話,則應在這個訊息處理函數中也加上斷點
· 注意Integration Environment中表單設計器的副作用。對於處在InitializeComponent中的代碼,如果需要做修改,盡量先將其搬到函數外面來,否則,不能保證修改過的代碼不被Integration Environment改回來或者刪掉
· 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
}
}