为什么一个项目有那么多BUG
经常与代码打交道,或多或少总会出些问题。一方面是自己相当然,过去是这样写的,没有问题,通常认为不太可能会出错,另一方面,项目时间紧迫,没有那么多时间去仔细考虑,每天有十几个功能等着你去实现,大多数的programmer会等程序忙完了,测试的时候再发现问题。
下面我列举一下,在项目开发中,我曾经犯过的错误,有的是我看到过的,我认为有不合理的地方。
1 时间的处理
举例:取当前时间是DateTime.Now属性,那么取当前时间的下个月的今天,你是否会用下面的代码
DateTime current=DateTime.Now;
DateTime nextMonth=new DateTime(current.Year, current.Month + 1, current.Day);
还有,取到昨天和明天的日期,是否是直接调用current.Day+1和current.Day-1。
2 数据库字段的返回值
如果要返回的数据库字段值为空,比如
SELECT LastName FROM Northwind.dbo.Employees WHERE EmployeeID=999
在程序中,下面的方法合理吗
DataSet ds = SqlHelper.ExecuteDataSet(sql);
if (ds.Tables[0].Rows.Count > 0){
if (ds.Tables[0].Rows[0]["LastName "].ToString() != "")
{ string name=ds.Tables[0].Rows[0]["LastName ”].Trim(); } }
有时候,为了方便,直接拿来就用
int age=Int32.Parse(ds.Tables[0].Rows[0]["Age”]);
3 SQL语句的写法
经常看到的以下的SQL语句的写法,有什么问题
1) “SELECT LastName FROM Northwind.dbo.Employees
WHERE EmployeeID=”+txtEmployeeID.Text ;
2 ) int EmplyeeID=Convert.ToInt32(txtEmployeeID.Text);
string sql=”SELECT LastName FROM Northwind.dbo.Employees WHERE EmployeeID={0} “;
sql=string.Format(sql,EmplyeeID);
4 SQL的WHERE参数的位置
有两种情况,一种是直接在界面的代码中拼接SQL WHERE参数,一种情况是在DAL方法中拼接SQL 参数
1) DateTime dtFrom = Convert.ToDateTime(txtDateFrom.Text);
DateTime dtTo = Convert.ToDateTime(txtDateTo.Text);
string where=” WHERE OTTime BETWEEN “+dtFrom + “ AND "+dtTo ;
2 ) DataTable tbl=OTReport.GetMonthReport(dtFrom ,dtTo );
然后在GetMonthReport同上面的代码一样,拼接SQL WHRER参数
这两种方法,各有什么问题?
5 比较字符串,你选用下面的哪一种方法
string obj=txtEmployeeName.Text;
1) int ret=String.Compare(obj,"CN\\SHLI",true);
2) “CN\\SHLI”==obj
3) obj.Trim().Equals(“CN\\SHHLI”, StringComparison.InvariantCultureIgnoreCase)
6 在需要表示业务处理状态的地方,还在用数字。
例如:表示申请,审批,拒绝,批准的状态,程序中还在用1,2,3作为标识。
1表示正在申请中,2 表是审批中,3表示拒绝
7 在需要用数值类型的地方,仍然还用string
例如,在SQL语句中无法直接得出一列值,需要在程序中判断后得出,于是多加一列,类型是string
DataColumn OTSummary = new DataColumn("OTSummary", typeof(string));
tbl.Columns.Add(OTSummary);
8 在一个ASP.NET 页面中,还在用静态变量保存临时的变量值。比如,一个页面中同时可以进行新增用户或修改用户两种业务,于是你需要一个静态变量,保存当前的动作状态是Insert还是Update。
public partial class OTDefault : System.Web.UI.Page {
static string Action= "Insert";
}
9 在界面层,还在用如下的方法转换用户输入的数据
比如,你需要用户输入员工编号,刚好你设计的员工表,员工编号的字段类型是int,而且是自增长
int EmployeeID=Convert.ToInt32(txtEmployeeName.Text);
或者更喜欢用 int EmployeeID=Int32.Parse(txtEmployeeName.Text);
10 在设定DropDownList当前选择值时,还是喜欢用SelectedValue
比如,你有一个DropDownList控件,绑定公司的所有部门的名称.这时,有一个用户A登陆到系统,你需要把DropDownList中的当前选择项目,设为他所在的部门,代码大概是这样写的ddlDepartment.SelectedValue=UserDAL.GetDepartmentName(CurrentUser)
11 因为需要对数值结果进行四舍五入,在精度要求保留小数点后面一位小数时,你使用方法
Math.Round,不错,方法选的很对。 可是你对下面的结果始终表示怀疑
Math.Round(3.44,1)=Math.Round(3.45,1)=3.4
12 喜欢用查询字符串,通常的情况是拿来即用,简单方便
string id= Request.QueryString["GUID"];
Guid key=new Guid(id);
13 在一个报表呈现界面中,你用AJAX控件,让用户选择日期区间,然后根据用户选择的时间区域,得到报表数据。有两个控件txtDateFrom, txtDateTo
<asp:TextBox ID="txtDateFrom" runat="server" Width="160"></asp:TextBox><asp:ImageButton ID="btnDateFrom" runat="server" Width="16" ImageUrl="Image/Calendar.png" Height="16"></asp:ImageButton>
<ajax:CalendarExtender ID="CalendarExtender1" runat="server" PopupButtonID="btnDateFrom" TargetControlID="txtDateFrom" Format="yyyy-MM-dd"> </ajax:CalendarExtender>
<asp:TextBox ID="txtDateTo" runat="server" Width="160"></asp:TextBox><asp:ImageButton ID="btnDateTo" runat="server" Width="16" ImageUrl="Image/Calendar.png" Height="16"></asp:ImageButton>
<ajax:CalendarExtender ID="CalendarExtender1" runat="server" PopupButtonID="btnDateTo" TargetControlID="txtDateTo" Format="yyyy-MM-dd"> </ajax:CalendarExtender>
编译,运行一点问题也没有。有一天,用户想查看过去某一天,该天发生的所有业务,于是用下面的代码来获取用户选择的那一天的时间
DateTime dtFrom = Convert.ToDateTime(txtDateFrom.Text);
DateTime dtTo = Convert.ToDateTime(txtDateTo.Text);
程序运行正常,可是什么数据也没有呈现出来。奇怪,明明当天发生过100多笔业务,怎么一笔数据也看不到。
14 为了返回一个字段的集合,还是习惯ArrayList.
比如,为了读取数据库中部门表中的所有部门的名称,如下代码
ArrayList list;
while(dataRead.Read())
{ list.Add(dataRead[“DepartmentName”].ToString()); }
有时候为了省事,也会这样做,直接返回一个数组
public static string [] GetDepartmentList(string DeparmentID)
15 不习惯于处理异常,只在开发阶段处理异常。
在界面层,对于可能会发生的异常,用cath把它截获,导致界面层里一堆一堆的try { } catch{ }
在业务层,不知道怎样的行为会导致异常,而又不愿意对方法的参数进行效验。
在数据访问层,发生数据库异常后,没有向上抛出,导致一个问题找了很久也没有找到答案。
16 同一个项目,因为不同的客户,对于同样的功能,有不同的需求。
于是直接把代码拷贝两份,分别维护。
比如,一个IPR项目,有两个客户需要使用,一个是客户A,一个是客户B。当IPR项目开发完后,负责维护客户A的IPR项目的开发员,就只负责维护他的那份拷贝,负责维护B客户的IPR项目的开发员,就只维护他的那份拷贝。
同样的功能有不同的需求。比如报表,同一个WIP报表,A客户要求的报表格式和B客户要求的格式不一样,而且报表中还要打上他们自己公司的Logo, A客户是要按生产组别呈现数据,B客户要求按照项目名呈现数据,也许还有一个潜在的C客户,他可能会要求报表按照工序来呈现WIP数据。
17 在数据库字段中很喜欢用自增的ID做主键,而且正在做一个存储配置信息的表,叫IPRParam.
ID是主键,当ID为1时 表示,当前正在使用的业务的最大的流水号,ID为2表示,当前的报表呈现方式,是按照生产组别,还是按照员工工号,于是,这样的自增ID不停的增长,在你的开发阶段,也从来没有发生过问题。上面两行数据的看起来想这样,
配置表终于做好了,程序也做好了,于是把这个配置数据表用SQL导入导出功能,导入到服务器中;出错,提示无法导入,一身冷汗。
于是,还想到可以用一个工具,把SQL表的数据转换为INSERT语句,于是拿来用一下,
很快就生成了对应的SQL语句,打开查询分析器,执行上面生成的SQL语句,还是报错。
难道一定要用SQL企业管理器,直接在服务器的数据库表中手工输入这些数据?
以上所列出的种种问题,不一定是bug,在有些情况下也不会有任何问题。
当这些问题隐藏在各种业务代码中的时候,你还能都注意到,并且避免吗?
欢迎大家补充开发中经常出现的错误,让我们做的项目少一些bug,稳定一些,再稳定一些。