近一年来受工作的关系看其他程序员的代码的机会变多了,学习了不少东西,但同时也发现很多问题,其中我遇到的最多的就是对错误的处理态度。
1) 从不拦截错误;
这可能是最原始且是最不好的行为,他们总是认为自己的程序肯定100%不会出现问题,因为他们坚信自己的代码考虑了一切可能的情况,这种理解我认为是非常危险的,翻翻你的代码,看看是否考虑了以下常见的情况:
l 在只允许输入数字的文本框里你拦截了键盘事件,但用户用右键粘贴了文本后你的程序正常吗?
l 设置了Locked属性的文本框用户一样可以粘贴,你的程序出问题了吗?
l 磁盘空间不足、目录只读、没有权限、目录名中有小数点、系统采用了短日期甚至不标准的格式;
l 明明事先检查了磁盘空间足够,可用户偏使用了什么磁盘配额,还是磁盘空间不足;
l 用户在你一段长时间的操作工程中等的不耐烦强行关机后重新运行你的这段程序;
l 你是否相信Access数据库或SQL Server数据库设置字段不能为空,但数据库就会莫名其妙的有NULL值在里面;
你所想象不到的情况太多了,所以说你永远不可能考虑所有的情况。
2) 总是拦截错误;
这种情况和第一种情况恰恰相反,他会不厌其烦的在每个过程中都添加On Error Goto ,然后出错的话报一个MsgBox框出来,我相信俩年以上五年以下工作经验99%的VB程序员基本上都是这么做的,也许马上就有人立即站起来说:你刚才还说不可能考虑每一种情况,所以要拦错,为什么马上反悔了呢?这里我想提出我的第一个重要观点:
尽量不要封杀任何的底层错误。
我们假设有4个过程A,B,C,D,其中A调用了B,B有调用了C,C又调用了D,如果我们在每个子过程中都拦截了错误,那么如果D发生了错误,程序将报告发生一个错误,显然这时D退出后,C其后的代码就不应该执行了,似乎他现在唯一的办法只有通过D返回False来表示失败,他才知道底层失败了,不能继续执行了,同样的道理C和B都要返回False来取消任务。
天啦,这岂不是要没有过程都要写成返回True和False了,看看你的程序,这可能吗?而且你将遇到一个非常尴尬的局面,你调用任何一个函数都要这样调用:
If B = False Then
A = False
Exit Sub
End If
我想你肯定觉得不爽,不仅如此你还有一个致命的问题:最上层的程序(注意是程序而不是用户)根本不知道底层什么地方失败了,什么原因失败了,他唯一知道了事就是失败了!啊,真惨。
既然这样我们回头看看底层不拦截错误的情况:
Public Sub A()
On Error Goto ErrorHandler
B()
'Do some thing
Exit Sub
ErrorHandler:
MsgBox(Err.Des cription)
End Sub
Public Sub B()
'Do some thing
C()
End Sub
Public Sub C()
'Do some thing
D()
End Sub
Public Sub D()
'Do some thing
End Sub
在这里B、C、D都没有拦截错误,且不需返回是否成功,他们只专注自己所做的工作,且你可以看见的,任何地方出错都将跳出返回上一级。
所谓错误,就是一个完整事务的失败,不管他是什么情况。一个数据库连接失败是错误,一个不符合商业规则的东西也是错误,在我们的程序中同样应该作为错误触发。例如:
If strName = "" Then
Err.Raise(vbObjectError + 23, "Cworker.UpDate", , "名称不能为空。")
End If
当然我同样将用户的取消作为错误出来,触发取消错误,例如:
If UserCancel Then
Err.Raise(vbObjectError + 10, "frmWorker.cmdCancel_Click", , "用户取消操作")
End If
我一般习惯上把错误分为致命错误、软硬件错误、取消错误和商业错误。商业错误分为:不符合商业规则错误、权限错误。
对于程序的错误出来分布我一般把所有函数分为:安全函数和不安全函数,比如一个窗口中有十种动作,我首先建立一个RunFunction过程,例如:
Public Sub RunFunction(ByVal vFun As EnumBusinFunction)
这个过程作为所有动作的入口,且统一出来错误,这个过程就叫安全函数,因为理论上调用他是不会播发错误的,然后所有事件中调用这个函数。在RunFunction函数中所调用的其他函数理论上应该都是不安全函数。
以上是我对错误的态度,说的很杂乱,请大家不要介意,我非常希望大家能够一起谈谈这个问题。
tansm@msn.com 谈少民 2001年11月30日于广州