这篇文章的起因是因为看到很多项目在设计上和功能实现上都很高大上,但是一些细节代码却不堪入目。本文准备从代码细节上规范代码。
此文不涉及命名规范和注释规范。另外如果有不实之处还望在评论区指出。
一、禁止使用加号拼接字符串
项目中总是看到用+号去拼接字符串,但是我觉得完全可以用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的地方禁止用""
原因:使用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) { if (id == string.Empty) { throw new Exception("ID不能为空"); } }
string str = "aaaaaaaa11111111aaaaa"; Console.WriteLine(str.Replace("1", string.Empty));
三、继承自IDisposable的非静态类一定要释放,能用using释放就必须用using
为什么是非静态类呢...因为我之前用.netcore发邮件的类也给它释放了,结果第二次发不了邮件了,所以静态的类还是不要释放了。。
原因:非托管资源必须手动释放,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中介的方式释放资源
原因:听说嵌套使用using会造成里面报错外面的释放不了
反例:
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; } }
五、循环内的中间变量声明到循环外面
原因未知,我之前TeamLeade告诉我的。求大佬补充原因
反例:
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("等待两分钟后输出"); }
八、方法禁止返回Task,使用async/await代替返回Task
原因未知,之前在某个博客上看到的。求大佬补充
反例:
public static async Task Main(string[] args) { Console.WriteLine(await GetName()); } public static Task<string> GetName() { return Task.FromResult("碌云"); }
正例:
public static async Task Main(string[] args) { Console.WriteLine(await GetName()); } public static async Task<string> GetName() { return await 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)} 不能为空"); } //...省略很多代码 }
暂且写到这里,如果后面想到了这篇博客还是会继续更新的。