這篇文章的起因是因為看到很多項目在設計上和功能實現上都很高大上,但是一些細節代碼卻不堪入目。本文准備從代碼細節上規范代碼。
此文不涉及命名規范和注釋規范。另外如果有不實之處還望在評論區指出。
一、禁止使用加號拼接字符串
項目中總是看到用+號去拼接字符串,但是我覺得完全可以用string.Concat()、string.Format()、StringBuilder、$、string.Join 等方式替代用加號拼接字符串。
原因:無論是從性能上還是可讀性來說都比+號好一點。(常量拼接除外,因為常量用+拼接編譯的時候會直接變成結果字符串。)
反例:
string str = "歡迎您,"; string name = "碌雲"; str += name; Console.WriteLine(str);
string name = "碌雲"; Console.WriteLine("歡迎您," + name);
string str = "歡迎您,"; string name = "碌雲"; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("<span>"); stringBuilder.Append(str + name); stringBuilder.Append("</span>"); Console.WriteLine(stringBuilder.ToString());
正例:
string name = "碌雲"; Console.WriteLine(string.Concat("歡迎您,", name));
string name = "碌雲"; Console.WriteLine($"歡迎您,{name}");
string str = "歡迎您,"; string name = "碌雲"; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("<span>"); stringBuilder.AppendFormat("{0}{1}", str, name); stringBuilder.Append("</span>"); Console.WriteLine(stringBuilder.ToString());
二、能用string.Empty的地方禁止用""
原因:可讀性更好,性能也更好。(性能可能是一樣的,性能這點爭議性比較大。)
反例:
public void GetUserInfoById(string id) { if (id == "") { throw new Exception("ID不能為空"); } }
string str = "aaaaaaaa11111111aaaaa"; Console.WriteLine(str.Replace("1", ""));
正例:
public void GetUserInfoById(string id) {
//一般看業務來的,實際開發中可能是用string.IsNullOrEmpty,這里只是舉例原代碼為""時可以改成這樣子。
if (id == string.Empty) {
throw new Exception("ID不能為空");
}
}
string str = "aaaaaaaa11111111aaaaa"; Console.WriteLine(str.Replace("1", string.Empty));
三、繼承自IDisposable的非靜態類一定要釋放,能用using釋放就必須用using
為什么是非靜態類呢...因為我之前用.netcore發郵件的類也給它釋放了,結果第二次發不了郵件了,所以靜態的類還是不要釋放了。。
原因:非托管資源必須手動釋放,using做了優化:哪怕內部報錯了還是會釋放 。(C#8.0開始可以直接在語句前寫using,點擊這里查看)
反例:
/// <summary> /// MD5加密 /// </summary> public string MD5Encrypt(string plainText) { return BitConverter.ToString(new MD5CryptoServiceProvider().ComputeHash(Encoding.Default.GetBytes(plainText))).Replace("-", string.Empty); }
正例:
/// <summary> /// MD5加密 /// </summary> public string MD5Encrypt(string plainText) { using (var md5c = new MD5CryptoServiceProvider()) { return BitConverter.ToString(md5c.ComputeHash(Encoding.Default.GetBytes(plainText))).Replace("-", string.Empty); } }
四、禁止嵌套使用using釋放資源
可以使用連續using或者利用IDisposable中介的方式釋放資源
原因:編譯后會生成多個try影響性能並且代碼也不美觀
反例:
private DataSet GetDataSet(string cmdText, CommandType cmdType, params SqlParameter[] parameters) { if (string.IsNullOrEmpty(cmdText)) return null; using (SqlConnection conn = GetSqlConnection()) { using (SqlCommand comm = new SqlCommand()) { using (SqlDataAdapter adap = new SqlDataAdapter(comm)) { PrepareCommand(conn, comm, cmdText, cmdType, null, parameters); DataSet data = new DataSet(); adap.Fill(data); return data; } } } }
正例:
/// <summary> /// 執行命令返回DataSet /// </summary> /// <param name="cmdText">要執行的命令</param> /// <param name="cmdType">命令類型</param> /// <param name="parameters">參數</param> /// <returns></returns> private DataSet GetDataSet(string cmdText, CommandType cmdType, params SqlParameter[] parameters) { //如果傳入進來的sql是空的則直接return if (string.IsNullOrEmpty(cmdText)) return null; //創建數據庫連接對象 using (SqlConnection conn = GetSqlConnection()) //創建數據命令對象 using (SqlCommand comm = new SqlCommand()) //獲取SqlDataAdapter using (SqlDataAdapter adap = new SqlDataAdapter(comm)) { //打開數據庫連接並初始化命令對象 PrepareCommand(conn, comm, cmdText, cmdType, null, parameters); //返回的data DataSet data = new DataSet(); adap.Fill(data); return data; } }
private DataSet GetDataSet(string cmdText, CommandType cmdType, params SqlParameter[] parameters) { if (string.IsNullOrEmpty(cmdText)) return null; SqlConnection conn = GetSqlConnection(); SqlCommand comm = new SqlCommand(); SqlDataAdapter adap = new SqlDataAdapter(comm); using (IDisposable a = conn, b = comm, c = adap) { PrepareCommand(conn, comm, cmdText, cmdType, null, parameters); DataSet data = new DataSet(); adap.Fill(data); return data; } }
五、循環內的中間變量聲明到循環外面
原因: 放在循環中聲明代碼會執行多次,放在外面只會聲明一次。
反例:
List<MenuTreeModel> list = new List<MenuTreeModel>(); foreach (var item in paMenus) { MenuTreeModel menu = new MenuTreeModel() { href = item.Url, icon = item.Icon, mid = item.Mid, target = item.Target, title = item.Name }; list.Add(menu); }
正例:
List<MenuTreeModel> list = new List<MenuTreeModel>(); MenuTreeModel menu; foreach (var item in paMenus) { menu = new MenuTreeModel() { href = item.Url, icon = item.Icon, mid = item.Mid, target = item.Target, title = item.Name }; list.Add(menu); }
六、for循環的第二段不要調用方法或者屬性
原因:因為每次循環都會進入第二段所以每次訪問還是有點效率問題的
反例:
List<string> list = new List<string>() { "碌雲" }; for (int i = 0; i < list.Count; i++) { Console.WriteLine(list[0]); }
正例:
List<string> list = new List<string>() { "碌雲" }; for (int i = 0, count = list.Count; i < count; i++) { Console.WriteLine(list[0]); }
七、用運算的方式增加代碼可讀性
原因:可讀性高,常量運算和直接寫常量性能是一樣的。(運算會被編譯器優化成常量,所以沒有性能損耗。)
反例:
public static async Task Main(string[] args) { await Task.Delay(120000); Console.WriteLine("等待兩分鍾后輸出"); }
正例:
public static async Task Main(string[] args) { await Task.Delay(1000 * 60 * 2); Console.WriteLine("等待兩分鍾后輸出"); }
八、在需要的時候才await
原因:await越少性能就越好,微軟的源碼中也有很多地方是直接返回的task,而不是await后返回。這個可以詳細研究下await/async的原理。
反例:
public static async Task Main(string[] args) { Console.WriteLine(await GetName()); } public static async Task<string> GetName() { return await Task.FromResult("碌雲"); }
正例:
public static async Task Main(string[] args) { Console.WriteLine(await GetName()); } public static Task<string> GetName() { return Task.FromResult("碌雲"); }
九、不要在循環中使用try/catch,應該把try/catch放到外層
原因:性能更好。具體原因未知,求大佬補充
值得注意的地方就是這樣子會導致如果發生錯誤了循環就進行不下去了,這個實際中還是看業務來吧。
反例:
for (int i = 0; i < 100; i++) { try { Console.WriteLine($"輸出{i + 1}"); } catch { Console.WriteLine("輸出字符串錯誤"); } }
正例:
try { for (int i = 0; i < 100; i++) { Console.WriteLine($"輸出{i + 1}"); } } catch { Console.WriteLine("輸出字符串錯誤"); }
十、盡量使用nameof獲取名稱
原因:可讀性強,並且如果被引用的地方發生變更程序會報錯,這樣就可以一起進行相應的更改。
反例:
public class User { public string Name { get; set; } } public User EditUser(User user) { if (string.IsNullOrEmpty(user.Name)) { throw new Exception("Name 不能為空"); } //...省略很多代碼 }
正例:
public class User { public string Name { get; set; } } public User EditUser(User user) { if (string.IsNullOrEmpty(user.Name)) { throw new Exception($"{nameof(User.Name)} 不能為空"); } //...省略很多代碼 }
十一、使用Environment.NewLine替代直接寫換行符
1.Windows 中的換行符"\r\n"
2.Unix/Linux 平台換行符是 "\n"。
3.MessageBox.Show() 的換行符為 "\n"
4.Console 的換行符為 "\n"
換行符還因平台差異而不同。
為保持平台的通用性,可以用系統默認換行符 System.Environment.NewLine。
出處:https://www.cnblogs.com/skykang/archive/2011/12/08/2281084.html
反例:
Console.WriteLine("碌雲\n歡迎您");
正例:
Console.WriteLine($"碌雲{Environment.NewLine}歡迎您");
暫且寫到這里,如果后面想到了這篇博客還是會繼續更新的。