下面这几个,是在实际开发或阅读中发现的一些问题,有些甚至是有很多年开发人员写出的代码,也是很多人经常犯的错误。各位可以看看,你有没有躺着中枪。
第一个,对整型变量进行非null判断。
// a 是int型 (不是int?) if(a != null){ //操作 }
个人点评:无意义判断,值类型永远不可能为null。
第二个,用static来保持页面回发
static int id; protected void Page_Load(object sender, EventArgs e) { if (Request.QueryString["ID"] != null && Request.QueryString["ID"].ToString() != "") { id = Convert.ToInt32(Request.QueryString["ID"].ToString()); } }
个人点评:这个不解释,不知道怎么说。但最近还真就遇到了,而且也不是什么小项目,WebForm无服务器控件开发。
第三个,用编程方式绑定数据控件时,数据源为DataSet时判断null而不判读DataSet内的Tables数。
DataSet ds = bll.GetList(); if (ds != null) { Repeater1.DataSource = ds; Repeater1.DataBind(); }
个人点评:当bll.GetList()返回的DataSet非null但里面没有包含数据表时,执行DataBind()方法时会报HttpException异常(IListSource 不包含任何数据源)。正确写法应该是
DataSet ds = bll.GetList(); if (ds != null && ds.Tables.Count > 0) { Repeater1.DataSource = ds; Repeater1.DataBind(); } //或 DataSet ds = bll.GetList()??new DataSet(); if (ds.Tables.Count > 0) { Repeater1.DataSource = ds; Repeater1.DataBind(); }
第四个,用编程方式绑定数据控件时,数据源为DataTable或List<T>时判断null。
DataTable dt = bll.GetList(); if (dt!=null) { Repeater1.DataSource = dt; Repeater1.DataBind(); }
个人点评:无意义判断,下面的写法没有任何问题,即使dt=null
DataTable dt = bll.GetList(); Repeater1.DataSource = dt; Repeater1.DataBind();
第五个
Model m = new Model(); m = bll.GetModel(id); m.name;
个人点评:以为只要声明时不为null,后面就不需要做非空非null判断了。万一第二步BLL层返回的model就为null呢?
第六个,在Repeater1_ItemDataBound中写这样的代码
Label lblPMID = (Label)e.Item.FindControl("lblPMID"); if (lblPMID.Text != "") { //操作 }
个人点评:低效,无意义判断,很可能出现NullReferenceException(未将对象引用设置到对象的实例)异常。
正确写法:
Label lblPMID = e.Item.FindControl("lblPMID") as Label; if (lblPMID!=null && lblPMID.Text != "") //视里面使用情况决定是否判断lblPMID.Text为“”或空白 { //操作 }
第七个
string txtName = Request["txtName"] == null ? "" : Request["txtName"].ToString(); string strWhere += "and ID=" + userId + ""; //userId是int if (txtName != "") { strWhere += " and NAME='" + txtName + "'"; } strWhere += " order by id desc"; //项目本身都是采用参数化查询的,这里是一些暴露给Web层的高级查询条件。
个人点评:1、值类型和字符串拼接会隐式装箱,2、SQL注入危险。正确做法是userId.ToString()并且过滤txtName中特殊字符,限制字符串长度。
注意,拼接SQL时过滤字符串并不能完全防止SQL注入,但很多时候在高级查询时拼接SQL是最简单也是最方便的,这时候过滤不应该只过滤一些指定的特殊字符,
比如只过滤单引号,等号,大于/小于/等于,空格,括号之类的危险字符。应该对除中文字符、英文字母、和数字外的所有字符全部过滤掉(视情况而定)。
并且严格限制字符串长度,一般查询时输入的关键字不会太长,如果用户输入的有空格,就拆分成多个条件。这样能尽可能的减小SQL注入的机会。
最后,给大家分享几个小经验,虽说有些只是语法糖,但却可以帮助我们更高效编写或阅读代码。
一、引用类型的null值很麻烦,因为类型为null时使用点运算符 (.)会报异常,所以经常要做非null判断,可以用?? null 合并运算符减少代码量。例如:
//写法一 int ID; if (Request.Form["ID"] != null && Request.Form["ID"].ToString() != "") { ID = Convert.ToInt32(Request.Form["ID"].ToString()); } //写法二 int id; if (int.TryParse(Request.Form["ID"]??"",out id)) { } //方法一 string userName2=string.Empty; if (Session["userName"]!=null) { userName2 = Session["userName"].ToString(); } //方法二 string userName1 = Session["userName"] == null ? "" : Session["userName"].ToString(); //方法三 string userName = (Session["userName"] ?? "").ToString();
二、Web项目中的所有Session或cookie最好统一放到一个类中管理。最重要的目的是把Session中索引名独立出来管理,也就是除了本类外的所有页面都不要输入Session名。
可能用语言表达不够直白,直接上代码。
看到很多人是这样,包括网上流行的一些很常见的辅助类库。
1 /// <summary> 2 /// Session 操作类 3 /// 1、GetSession(string name)根据session名获取session对象 4 /// 2、SetSession(string name, object val)设置session 5 /// </summary> 6 public class SessionHelper 7 { 8 /// <summary> 9 /// 根据session名获取session对象 10 /// </summary> 11 /// <param name="name"></param> 12 /// <returns></returns> 13 public static object GetSession(string name) 14 { 15 return HttpContext.Current.Session[name]; 16 } 17 /// <summary> 18 /// 设置session 19 /// </summary> 20 /// <param name="name">session 名</param> 21 /// <param name="val">session 值</param> 22 public static void SetSession(string name, object val) 23 { 24 HttpContext.Current.Session.Remove(name); 25 HttpContext.Current.Session.Add(name, val); 26 } 27 /// <summary> 28 /// 检测session是否存在 29 /// </summary> 30 /// <param name="name"></param> 31 /// <returns></returns> 32 public static bool CheckSession(string name) 33 { 34 try 35 { 36 if (GetSession(name) == null) 37 { 38 return false; 39 } 40 else 41 { 42 return true; 43 } 44 } 45 catch 46 { 47 return false; 48 } 49 } 50 }