如何才能写出高质量的VB源代码

正如不是有鼻子有眼有嘴巴就可以拼凑出美女来一样,VB代码也是一样.看过论坛上的许多源代码,也包括很多摆在书架上的源代码,真是如梗在喉,不得不一吐为快.

一、高效源代码的含义

       什么才是“高质量代码”?现在已不是DOS时代了,没有人再会为节省1K的内在而惮思竭虑了。有人会说,如果一套好的源代码执行的结果比一套差的源代码快个几秒钟,没有任何意义,因为单是电脑快慢的差别就足以抵消这一点了,最重要的是在最短的时间内完成客户的订单,按时交货。应该承认这种观点的现实意义,但并不代表我们就可以随意涂鸦了。见过太多的所谓程序员在完成几个项目之后离职跳槽走了,原公司可就惨了,没有人能够看懂他的代码,如果要完全读懂他的代码所要的花费还不如重新写一遍呢。这种代码同垃圾又有什么区别?我们即便不做IT绅士,但也没有必要一定做IT农民吧。

       现在的程序员有这么两种类型:孤芳自赏型和我行我素型。孤芳自赏型的程序员看自己的程序源代码就好象在看一件稍有暇疵精美的工艺品,总是不断地优化、优化,客户订单也总是一拖再拖;我行我素型从来不按规定也代码(以小型软件公司为主,这些公司基本上没有做系统架构的,都是外面接到了单临时拼凑几个人写代码,交货收钱后大家做鸟兽散),我行我素型是不折不扣的IT农民,我的代码就是垃圾了,那又怎样?

     梁肇新在《编程高手箴言》一书中写道: 真正的商业程序绝对是规范的。你写的程序和他写的程序应该格式大致相同,否则谁也看不懂。

     至于规范化代码编写不是本文的重点,类似于《某某软件VB编码规范》之类的文档在网上很多,相信大家都可以找到。我在这里主要就代码效率的编写方法和大家做些交流。

二、若干常见问题

     1、尽量用变量代替属性

          请看下面一段代码:          

    Dim i(100) As Integer
    Dim j As Integer
   
    For j = 0 To 100
        i(j) = j
    Next
    For j = 0 To 100
        Text1.Text = Text1.Text + CStr(j)
    Next

    高效的做法是:

    Dim i(100) As Integer
    Dim j As Integer
    Dim s As String
   
    For j = 0 To 100
        i(j) = j
    Next
   
    s = Text1
    For j = 0 To 100
        s = s + CStr(i(j))
    Next
   
    Text1 = s

    比较常见的低效代码是在一段循环中反复使用属性.有人测试过,变量的存取速度是属性存取速度的20倍!

     2、32位下最快的数据类型

            WIN2000及WINXP都是32位操作系统,在32位操作系统中long是最快速的数据类型,特别是在FOR ……NEXT循环中。认识到这一点对于VB新手来说可能并没有太多的困难,但对于从WIN98下就从事VB开发的老程序员来说也许反倒有问题!WIN98是16位操作系统,WIN98下最有效的数据类型为integer,现在网上可以找到的汗牛充栋的VB源码最常见的就是“Dim i As Integer",这是一个编程习惯的问题,必须改正过来。我也见过一些在WIN98下运行正常的程序在WIN2000下出错(比如金算盘9I就曾经有过一个BUG,在显示分辨率为1024*768下即报错,在800*600下运行正常,当时我百思不得其解,客户又在催,我干脆对它来了个大卸八块,最后发现是由于程序退出时,注册表中记录了MDI的位置及大小,但数据类型定义为integer,改为DOUBLE后问题解决)。

         另外补充一点:64位操作系统发布在即,在64位操作系统下,INTEGER相当于32下的LONG,那时,Integer反倒是最快速的数据类型了:)

     3、尽量避免使用变体

有一篇著名的网文《VB之道》,里面有这么一段话:

 一个弟子向大师倾诉程序编译中频繁的数据类型报错,问大师:“使程序永远没有数据类型错误,可能吗?”
大师回答:“可以做到。”于是将所有变量类型设置为Variant。
弟子运行程序,高兴地发现没有一处错误,但系统很快死机。

————————————————————————————————

变体型的变量需要16个字节的空间来保存数据,另外OBJECT也存在同样问题。我有时觉得定义数据类型就好象开车,你什么速度就挂什么档,最合适的才是最好的。低档高速固然毁车而且费油,但高档低速同样会产生积炭等问题。那我用自动档好了,就好象Variant一样,多省事啊。省事是省事了,油耗上去了,变速箱一坏,光维修变速箱就得2~3W大元。

     4、尽量使用数组少用集合

           还是那篇网文《VB之道》,里面还有这么一段话:

           两个程序员为应该采用集合还是数组争论不已。
其中一个程序员找到大师:“集合不好吗?我可以准确地定位其中任何一个成员。”
大师回答:“你是正确的。”
另一个程序员也找到大师:“数组不好吗?我可以对其进行顺序访问。”
大师回答:“你是正确的。”
前一个程序员听说了,和另一个程序员一起来找大师:“我们俩到底谁是对的?”
大师回答:“你们是正确的。”
两个程序员对别人说:“谁说大师有学问呢?”

————————————————————————————————————

呵呵,的确够精彩,可我不得不告诉您:除非您必须使用集合,否则应尽量使用数组,数组是VB程序中最有效率的数据结构。有人做过测试,数组的存取速度是集合的100倍。

     5、使用MOVE方法

           例:ME.LEFT=(SCREEN.WIDTH-ME.WIDTH)/2):ME.TOP=(SCREEN.HEIGHT-ME.HEIGHT)/2)

           '将一个窗体在屏幕上居中显示,很多程序员都会这样写的吧,但是应该改成这样“ME.MOVE(SCREEN.WIDTH-ME.WIDTH)/2),((SCREEN.HEIGHT-ME.HEIGHT)/2)”,这样速度更快!理由在于按ME.LEFT的方法系统再判断窗体的四个坐标的属性。

     6、及时销毁对象

           (1)对数据库进行操作时需及时销毁的对象

例:

Public Function Connect() As Boolean  '连接数据库
On Error GoTo err
Dim strConn As String

    '如果连接标记为真,则返回。
    If Connect = True Then Exit Function
   
    Set g_Cnn = New ADODB.Connection
    strConn = "Driver={SQL Server};Server=" & g_ServerName & _
               ";Uid=fyming;Database=" & g_DataName
    g_Cnn.CursorLocation = adUseClient
    g_Cnn.Open strConn
   
    '设置连接标记
    Connect = True
 
Exit Function

err:
    MsgBox err.Description

End Function

______________________________________________________

Public Function Disconnect() As Boolean  '断开与数据库的连接
On Error GoTo err

    '如果连接标记为假,标明已经断开连接,则直接返回
    If Connect = False Then
       Exit Function
    End If
     
    '断开连接
    g_Cnn.Close
    Set g_Cnn = Nothing
   
    Disconnect = True
   
Exit Function

err:
  MsgBox err.Description
 
End Function

_____________________________________________

Public Function GetPageSize(ByVal lngCurUserID As Long, _
                            ByVal lngErrCode As Long) As Long  '取得页面大小
On Error GoTo err

    Dim cd As ADODB.Command
    Dim strValue As String
       
    If Connect = False Then Exit Function
   
    Set cd = New ADODB.Command
    cd.CommandText = "cus_GetFiledValue"
    cd.CommandType = adCmdStoredProc
    Set cd.ActiveConnection = g_Cnn
   
    cd.Parameters("@v").Value = 2
    cd.Parameters("@strFieldName1") = "FrmClause"
    cd.Parameters("@lngCurUserID") = lngCurUserID
    cd.Execute
    lngErrCode = cd.Parameters("@lngErrCode").Value
    If lngErrCode <> 0 Then GoTo aa
    GetPageSize = cd.Parameters("@lngPageSize").Value
       
aa:
    Disconnect

Exit Function

err:
    MsgBox err.Description
    Exit Function
   
End Function

____________________________________________________

上面三个函数中,第一个函数用来打开数据库,第二个函数用来关闭数据库.大家注意看第三个函数:先判断Connect 是否成功,连接成功之后对数据库进行操作(本例用来取出某个窗体流水记录每页行数),最重要的是千万别忘了Disconnect哟!

当然你如果不Disconnect,程序也不会出错,但为什么还要做这一步呢?我们知道,两层架构的C/S程序,客户端经常与数据库一直处于连接状态,这在50个以下并发用户可能没什么问题,而且速度还比较快,但是50个以上并发用户环境下这种系统的性能非常值得怀疑.

三层架构的C/S程序,通常把与数据库打交道的(比如上面的打开数据库、关闭数据库、从数据库中取值以及没有列出的新增、修改、删除等等)动作均封装成DLL,把结果集以数组或者XML文件形式返回给客户端,说得再详细点的话,就是客户端在收到数组或者XML文件之后,把对窗体控件的操作动作(比如载入数据、刷新窗体等等)封装成一个类,这样一来,在工程中的FORM中真的没有多少代码可写的了!呵呵。

俺刚开始做C/S三层架构MIS时,有个疑问:中间层回传客户端的数据以什么形式存在(那时XML才刚刚提出)?如果是数组,那么假如有几千条甚至几万条数据,此数组该有多大?网络开销怎么能够承受?后来才知道大量的工作其实是要在数据库中完成的(比如数据分页处理、存储过程、自定义函数、错误值返回等等),原先我对于数据库的知识只限于“select * from table where ……”,呵呵,真是惭愧!越扯越远了,关于三层架构的问题我有专题讲解,在些不再赘述。

其实做三层架构的应用程序从技术上讲并不难,难的在于您能够想到通过中间层来与数据库打交道,客户端直接与中间层打交道而不需要理会数据库。送您一句话:头可断,血可流,fyming思想绝不可丢!有了这种思想,甭说三层架构了,就是做五层、六层或者七八层架构还不是手到掂来(如果有需要的话)?

在此,除了上面所说的及时关闭你的RS和CONN处,我再一次强调:如果您目前还在用数据控件(比如Data1、ADODC等等)来与数据库打交道的话,那么马上把它扔了吧,否则就是再过100年,您还是一个菜鸟的水平!

 

           (2)对于窗体对象的销毁

                一个窗体在UNLOAD之后,其实并没有真正从内存中移去,要养成一个良好的编程习惯:SET   FORM=NOTHING。             

           (3)对于数组的销毁

                道理同上,解决方法亦相同:SET   Array()=NOTHING

     7、检查字符串是否为空的方法

            例:判断一个TEXT是否为空是很多程序都要用到的,您是怎样处理的?是 IF TRIM(TEXT)="" THEN吗?我要告诉您,正确的写法应该是:IF LEN(TEXT)=0 THEN……注意:进行字符串比较需要的处理量甚至比读取属性还要大!

            在这里我封装了一个自定义函数 txtAllCheckBlank,该函数的功能是在你进行数据存盘前对当前窗体所有TEXT进行非空检查,如果有空值,则报警并使该为空的TEXT获得焦点,一并送给大家:)

'检查本窗体内所有文本框的 Text 是否有空值的
'参数 objForm 为当前窗体对象
'参数 strMessage 为弹出的消息
Public Function txtAllCheckBlank(ByVal objForm As Form, _
                                 ByVal strMessage As String) As Boolean
On Error GoTo 0

    Dim objObject As Object
   
    txtAllCheckBlank = False
   
    For Each objObject In objForm
        If TypeOf objObject Is TextBox Then
            If Len(Trim(objObject.Text)) = 0 Then
                MsgBox strMessage, vbInformation + vbOKOnly
                txtAllCheckBlank = False
                txtFocus objForm, objObject.Name
                objObject.SetFocus
                Exit Function
            End If
        End If
    Next
   
    txtAllCheckBlank = True
   
End Function

——————————————————————————————

'某文本框获得 Focus 焦点并且选中该TXT中已有内容
'参数 objForm 为当前窗体对象
'参数 strName 该文本框名称
Public Function txtFocus(ByVal objForm As Form, _
                            ByVal strName As String) As Boolean
On Error GoTo 0

    Dim objObject  As Object
   
    txtFocus = False
   
    '遍历窗体上的对象,直至找到指定的文本框,并设置其 Focus 属性
    For Each objObject In objForm
        If TypeOf objObject Is TextBox Then
            If objObject.Name = strName Then
                objObject.SetFocus
                objObject.SelStart = 0
                objObject.SelLength = Len(objObject)
                Exit Function
            End If
        End If
    Next
   
    txtFocus = True
   
End Function

——————————————————————————————————————

把上述两函数放入一标准模块,在FORM中直接引用即可!

另:

txtFocus objForm, objObject.Name
objObject.SetFocus

为什么要  txtFocus objForm, objObject.Name?直接objObject.SetFocus不就行了吗?可是你有没有想过:如果操作员是用空格打入几个空格,看起来什么都没有,但LEN(TEXT)肯定通不过,通过txtFocus函数处理一下,可以把这几个空格反黑表现出来,可以更直观地提醒操作员。此外,您可以用我的自定义函数txtFocus来实现您需要反黑某个文本框的内容的功能要求哦!

 8、其他

(1)ADO的优化

RS.ADD、RS.UPDATE、RS.DELETE等动作最好改为SQL在数据库端执行,这样能够大幅提高您的系统的性能。我的程序中,目前把几乎所有对于数据库的操作都做成了存储过程,在客户端只是发几个参数到数据库,其余的工作都由数据库完成了,并且通过返回值对操作的结果进行跟踪。这样的好处是一方面可以真正体现胖服务器/瘦客户机的思想,另一方面便于程序员的分工协作,对于数据库有专长的可以专心设计数据库,对于VB代码编写有专长的可以专心代码编写,不亦乐乎?

(2)关于WITH

有人曾经说“最好不要用WITH,它会在编译时形成一个分支,影响系统运行速度。”即使是这样,我还是要推荐您这样来写程序,代码简洁而且易读。

(3)关于SELECT CASE……END SELECT

如果有类似于IF……THEN……ELSE IF ……THDN ……END之类的代码,还是请您改写成SELECT CASE……END SELECT吧,这样的算法好处在于一方面代码变得清晰易懂,另一方面也不用一步步地去做IF的判断。

(4)计算取出的记录集有多少条记录

常见到很多程序员这样来写代码:

rs.movefist

do while not rs.eof

i=i+1

rs.movenext

loop

debug.print i

这样的写法下,每次rs.movenext时都要判断rs是否EOF,是种低效的写法。应该这样改写:

rs.movelast

i=rs.absolutePosition

rs.movefirst

for j=1 to i

      ……

     rs.movenext

next 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值