[Previous] [ Next ]
It's not enough just to add comments to a procedure; you need to write good comments. Comments that simply reiterate what the code does add almost nothing to the code. In fact, if you're using good naming techniques to create self-documenting code, these types of comments add absolutely nothing. For instance, look at the comment in the following procedure. It simply reiterates the code itself; the variable names are even used within the comment. This comment documents the mechanics of the code rather than the purpose of the code or the reasoning behind it.
PrivateSub cmdStartSale_Click() Dim intAge AsInteger Const c_LegalAge=21 intAge=txtAge.Text '*IfintAge>=c_LegalAgethenprocessthesale. If intAge>=c_LegalAge Then Call ProcessSale EndIf EndSub |
When you find yourself writing a comment that includes the literal name of a variable, stop and rethink the comment. Unless you're documenting the variable itself or reminding the reader of the purpose of an obscure variable, you should refrain from using variable names directly within a comment. When a variable name is used in a comment, the comment probably reiterates the code. Instead, you should document the purpose of the code or the reasoning behind it. The following procedure is identical to the previous one, but in this case the comment correctly annotates the code:
PrivateSub cmdStartSale_Click() Dim intAge AsInteger Const c_LegalAge=21 intAge=txtAge.Text '*Ifthebuyerisoflegalagetopurchasealcohol, '*processthesale. If intAge>=c_LegalAge Then CallProcessSale EndIf EndSub |
NOTE
Comments should describe the purpose of a section of code, not the mechanics of how it accomplishes that purpose ”the why rather than the how.
At times it might be necessary to violate good coding principles. When you encounter such a situation, document what you are doing and why, using an inline comment. For instance, say you are unable to write your own sort routine or are under severe time constraints ( laziness doesn't count), so you use a hidden list box control to sort a set of values. You place the list box on a form and set its Sorted property to True and its Visible property to False. You then populate the list box with all the elements to sort using the list box's AddItem method. Finally, you retrieve the newly sorted values from the list by using a loop to reference the list box's List property. This is clearly a hack, but it works. In cases such as this, it is imperative that you document what you are doing and why. The following code shows how you might document such a process.
'*AddthenamesinthenamearraytothelstSort '*listbox.IthasitsSortedpropertysettoTrue, '*soloopingthroughthelistitemswillreturn '*thevaluesinthearrayinsortedorder. For intIndex=1 ToUBound (a_strNames) lstSort.AddItema_strNames(intIndex) Next intIndex |
Due to the nature of Microsoft Visual Basic, some errors are unavoidable and others are absolutely necessary. For instance, you might decide that rather than attempting to see whether a record exists in a database before trying to save a new record (to avoid creating duplicates) it would be quicker to attempt to save the record and trap the error if the record is a duplicate. The following code shows this situation. When you expect an error in code and you deliberately trap for it, fully document why you're trapping the error and what error you expect. If more than one error might occur, document the others as well. (See Chapter 7, "Error Handling," for more information on trapping errors.)
'*Traptheerrorincasetheuserisattemptingtosavea '*duplicaterecord. OnErrorResumeNext Err.Clear dbSales.ExecutestrSQL,dbFailOnError Const c_DuplicateRecord=3022 '*Ifanerroroccurred,determinewhetheritwasbecauseof '*tryingtosaveaduplicaterecord. If Err.Number<>0 Then '*Seewhethertheerrorwasexpected. If Err.Number=c_DuplicateRecord Then '*Theuserattemptedtosaveaduplicaterecord. '*Telltheuserandgetout. MsgBox"Theitem'"&txtItem.Text&"'alreadyexists"&_ "inthedatabase.",vbInformation GoTo PROC_EXIT Else '*Thisisanunexpectederror;notifytheuser. Call ShowError(Me.Name,"ChangeCode",Err.Number,_ Err.Description) GoTo PROC_EXIT EndIf EndIf '*Turnregularerrortrappingbackon. OnErrorGoTo PROC_ERR |
Sometimes, a statement might cause an error that is always benign . This can happen with Visual Basic's venerable SetFocus method. This method, which places the cursor into a specific control, has many applications. For example, you can use SetFocus when the user switches among tabs in a dialog box to ensure that when a new tab is displayed the first control on the tab has the focus. You can also use SetFocus when you validate data to put the cursor in a text box with invalid data so that the user can quickly correct the problem. While SetFocus is used liberally in most applications, many developers fail to precede each call with On Error Resume Next . You should do this because if a form or a parent control hasn't fully painted itself, SetFocus might fail with an Error 5. This error doesn't hurt anything, and in most cases it's perfectly acceptable to ignore the error. Since this particular situation happens frequently, it's probably not advisable to document each occurrence, but when you do want to document it you can use a comment such as the one shown here:
'*Ignoreanyerrorsthatoccurbecausetheformisnotreadyforthe '*controltoreceivethefocus. OnErrorResumeNext txtName.SetFocus '*Turnregularerrortrappingbackon. OnErrorGoTo PROC_ERR |
One approach to commenting code is to start a procedure by writing the comments first. You can write full sentence comments or pseudocode if you prefer. Once you outline the code with comments, you can write code between the comments. As you write the procedure, you might have to adjust your comments. After you write the procedure, convert all pseudocode comments to standard sentences. The following is a shell of a procedure that consists of comments only:
PublicSub FloodFill(X AsSingle ,Y AsSingle ) '*Purpose:Fillanareaonthebitmapwithacolor. '*Accepts:XandY-coordinatesonthebitmapwhere '*thefillshouldbegin. '*SettheFillStyleofthepictureboxtosolidandthe '*FillColortothecurrentdrawingcolor. '*Gettheboundarycolorforthefill.Thisisthecurrentcolor '*atthespecifiedcoordinates. '*UsetheExtFloodFillAPIcalltoperformafastfloodfill, '*changingalladjacentpixelsthathavetheboundarycolor '*tothecurrentdrawingcolor. EndSub |
The initial comments are like an outline. After you write them, you can fill in the outline with the necessary code. The following is the finished procedure with code inserted between the comments. The comments do not simply repeat the code; they explain what is occurring. In this example, the comments did not need to be changed or moved, but that won't always be the case.
PublicSub FloodFill(X AsSingle ,Y AsSingle ) '*Purpose:Fillanareaonthebitmapwithacolor. '*Accepts:XandY-coordinatesonthebitmapwhere '*thefillshouldbegin. OnErrorGoTo PROC_ERR Dim lngResult AsLong Dim lngBoundaryColor AsLong '*SettheFillStyleofthepictureboxtosolidand '*theFillColortothecurrentdrawingcolor. picPreview.FillStyle=vbFSSolid picPreview.FillColor=m_lngDrawColor '*Gettheboundarycolorforthefill.Thisisthecurrentcolor '*atthespecifiedcoordinates. lngBoundaryColor=picPreview.POINT(X,Y) '*UsetheExtFloodFillAPIcalltoperformafastfloodfill, '*changingalladjacentpixelsthathavetheboundarycolor '*tothecurrentdrawingcolor. lngResult=ExtFloodFill(picPreview.hdc,X,Y,_ m_lngBoundaryColor,FLOODFILLSURFACE) PROC_EXIT: ExitSub PROC_ERR: MsgBox"Error:"&Err.Number&vbCrLf&Err.Description ResumeNext EndSub |
Some developers adopt formatting styles for comments that, while attractive, hinder the development process. It can be tempting to go overboard with comment formatting. A common example of such overzealousness is the use of formatting characters to create a line before or after comments. I call these comment lines solid-character comment lines . For instance, the asterisk (*) ”one of the most common formatting characters and my personal favorite ”is often overused in this way. Consider the comments in the following code fragment:
'******************************************************* '*Retrievethelengthsofthelegsoftherectangle. '******************************************************* sngXLeg=Abs(rectBound.Right-rectBound.Left) sngYLeg=Abs(rectBound.Bottom-rectBound.Top) '******************************************************* '*Makesuretherectangleisavalidrectangle. '******************************************************* If (sngXLeg=0) Or (sngYLeg=0) Then '******************************************************* '*Thisisnotavalidrectangle,sogetout. '******************************************************* GoTo PROC_EXIT EndIf '******************************************************* '*Populatethecirclestructurewiththedatathatdefines '*thecircle. '******************************************************* With udtCircle .Aspect=Abs(sngYLeg/sngXLeg) .xCenter=rectBound.Left+(rectBound.Right-rectBound.Left)/2 .yCenter=rectBound.Top+(rectBound.Bottom-rectBound.Top)/2 '******************************************************* '*Determinetheradiususingthelongerlegoftherectangle. '******************************************************* If sngXLeg>sngYLeg Then .Radius=sngXLeg/2 Else .Radius=sngYLeg/2 EndIf EndWith |
Wow ”all of those asterisks can give you a headache . If you leave your Visual Basic settings at their defaults, you'll see green all over the code window.
The color coding of comments is one of my favorite Visual Basic features. It seems so simple as to be almost silly, but if you've ever coded complex applications in Microsoft Access 2.0, which doesn't include a color-coded editor, you know where I'm coming from. I'd sooner quit developing than give up the color-coded editor. When scanning a procedure, it's great to be able to distinguish the comments in green text. But unnecessary comment lines decrease the ratio of usable green text to total green text.
In his book The Visual Display of Quantitative Information (Graphics Press, 1992), Edward Tufte discusses what he calls data ink, "the nonerasable core of a graphic." Nondata ink includes such elements as elaborate grid lines and detailed labels. Tufte discusses the necessity of a high data ink to total ink ratio. You can think of comments in much the same way.
Sometimes it makes sense to call attention to a comment by using solid-character comment lines, but in such cases they should be reserved for major comments, never minor comments. The solid-character comment lines in the following code are still overkill, but at least they make more sense by calling attention to the major elements of the procedure.
'******************************************************* '*Retrievethelengthsofthelegsoftherectangle. '******************************************************* sngXLeg=Abs(rectBound.Right-rectBound.Left) sngYLeg=Abs(rectBound.Bottom-rectBound.Top) '******************************************************* '*Makesuretherectangleisavalidrectangle. '******************************************************* If (sngXLeg=0) Or (sngYLeg=0) Then '*Thisisnotavalidrectangle,sogetout. GoTo PROC_EXIT EndIf '******************************************************* '*Populatethecirclestructurewiththedatathatdefines '*thecircle. '******************************************************* With udtCircle .Aspect=Abs(sngYLeg/sngXLeg) .xCenter=rectBound.Left+(rectBound.Right-rectBound.Left)/2 .yCenter=rectBound.Top+(rectBound.Bottom-rectBound.Top)/2 '*Determinetheradiususingthelongerlegoftherectangle. If sngXLeg>sngYLeg Then .Radius=sngXLeg/2 Else .Radius=sngYLeg/2 EndIf EndWith |
Solid-character comment lines pose additional problems. How many characters should the solid-character comment line contain? There's no doubt that they look better when they all contain the same number of characters, but what about when they're used before or after a short comment line? Or, more important, what about when they surround a longer comment, as they do in the previous example? Trying to maintain a consistent and attractive appearance with these solid-character comment lines quickly becomes tedious. Also, if you manually type each line, you're wasting time. The only alternatives to typing each line individually are to copy and paste the lines ”also tedious ”or to use some sort of code-formatting add-in.
Far worse than solid-character comment lines are formatting characters on the right side of comments that create comment blocks or boxes. You've probably seen such comments, and you might have written a few. If you've ever maintained code that has these sorts of comments, chances are you've given up on them ”and for good reason. Take a look at this comment:
'************************************************************* '*Iftheuserclickstheleftbutton,getthecoloratthe* '*currentcoordinatesandassignitastheForeColor.If* '*therightbuttonhasbeenclicked,getthecolorunder* '*thepointerandassignitastheBackColor.* '************************************************************* |
Sure, the asterisks on the right look nice, but do they add anything to the comment? Actually, yes ”more work for the person writing or editing the comments. Notice the white space after the last word on the second line. As I was writing this comment, I typed the next word the, only to discover that it would run into the last asterisk. I was then faced with a decision and had to consider the following options:
After I decided to put the word the on the next line and resume typing, I encountered the exact same problem ”oddly enough, with the exact same word. Isn't this fun? I added tedium to what many already call a tedious process. If writing comments was always this much work, I can see why some would choose to skip it altogether. Although the use of formatted lines in front of or after a comment can be justified to call attention to major events in code, the use of end-line formatting characters to create comment blocks is never justified. It does nothing but add extra work.
In the old days of Basic, you denoted a comment by starting a line with the word REM (for Remark). Visual Basic still supports the use of REM , but you shouldn't use it. Using REM clutters the comment, creates wasted green space, necessitates more typing, can confuse add-in code formatters, and in general just looks bad. Instead of REM , use the apostrophe (') ”and, perhaps, as I do, an asterisk (*) ”to denote a comment:
'*Ifthisisanewaccount,setupthenecessary '*defaultinformation.Iftheaccountexists,place '*therecordineditmode. If blnAddNew Then EndIf |
9.7.1 Use special characters to identify a comment's author or indicate its temporary status. In a multiple-developer environment, it's often desirable to know which developer wrote a specific comment. While you could store revision information in the procedure comment header, this is a cumbersome process and doesn't help when different developers work on different pieces of the same procedure.
A great solution to the problem of identifying a comment author is to assign a unique formatting character to each developer and to have the developer follow each remark character (') with his or her assigned formatting character. Once you get into the habit of doing this, it becomes second nature. This approach is useful only in departments with a small number of programmers. When you use a scheme like this, you should create a key of the users and their formatting characters in a global module. It might be a good idea to keep the authors' contact information there as well.
Correct:
'*Purpose:Createtabstopsinalistbox. '*Accepts:lstControl-thelistboxinwhichtosetthetabstops '*lngTabs-thenumberoftabstopstoset '*strStops-astringcontainingthecharacter '*positionsofthetabstops OnErrorGoTo PROC_ERR |
Also correct:
'$Purpose:Createtabstopsinalistbox. '$Accepts:lstControl-thelistboxinwhichtosetthetabstops '$lngTabs-thenumberoftabstopstoset '$strStops-astringcontainingthecharacter '$positionsofthetabstops OnErrorGoTo PROC_ERR |
You should also use a special character for temporary comments or notes. Temporary comments can be useful when you comment out a section of code while debugging or when you need to write more code later. In such cases, you should use a formatting character or a word to mark the comment as temporary. Use the comment designator consistently to make it easy to search a project for temporary comments. The character or word that you use is entirely up to you, but be sure that everyone working with the code uses the same format.
Correct:
'#SECURITYSTILLNEEDSTOBEIMPLEMENTED! strSQL="DELETE*FROMtblContacts;" dbContacts.ExecutestrSQL,dbFailOnError |
Also correct:
'NOTE:SECURITYSTILLNEEDSTOBEIMPLEMENTED! strSQL="DELETE*FROMtblContacts;" dbContacts.ExecutestrSQL,dbFailOnError |
NOTE
When you use formatting characters to denote something about a comment, document exactly what your standards are and share them with all developers.
Comments are meant to be read by humans , not computers. Strive to make your comments intelligible. Keep in mind that a comment that is hard to understand is not much better than no comment at all. Also, as I've said throughout this chapter, comments are documentation. Just as documentation for an application must be clearly written, code comments should also follow good writing guidelines.
9.8.1 Use complete sentences. While it's not necessary (and probably not advisable) to write paragraphs of comments, you should strive to write your comments in complete sentences. When developers write comments in phrases or sentence fragments , what they consider necessary information often falls short of what readers want or need to see. When you write comments in complete sentences, you force yourself to fully analyze the comment. Remember that excellent comments explain the general flow and purpose of a procedure even when stripped from the code they accompany.
Incorrect:
'*Doesuserhaverights? IfNot (objApplication.Security.CanDeleteAccounts) Then MsgBox"Youdonothavesecurityrightstodeleteaccounts.",_ vbInformation GoTo PROC_EXIT EndIf '*Confirm If MsgBox("Deletethisaccount?",_ vbYesNo Or vbCritical)=vbNo Then GoTo PROC_EXIT EndIf |
Correct:
'*Iftheuserdoesn'thavesecurityrightstodeleteanaccount, '*saysoandgetout. IfNot (objApplication.Security.CanDeleteAccounts) Then MsgBox"Youdonothavesecurityrightstodeleteaccounts.",_ vbInformation GoTo PROC_EXIT EndIf '*Askforconfirmationbeforedeleting,andgetoutiftheuser '*doesn'twanttodelete. If MsgBox("Deletethisaccount?",_ vbYesNo Or vbCritical)=vbNo Then GoTo PROC_EXIT EndIf |
9.8. Avoid using abbreviations. Unless your organization defines a documented set of words to abbreviate, you should avoid abbreviating words in your comments. Abbreviations often make comments harder to read, and people often abbreviate the same words in different ways, which can lead to confusion. If you must abbreviate, be very, very consistent. Say you have a human resources application that manages employees . Because the word employee appears in so many places, you might choose to abbreviate it as Emp. If you must do this, make sure that you do it consistently and that all other members of your team use the same abbreviation.
Incorrect:
'*EnablethedelAcctmenuitem. ActiveBar.Tools("DeleteAccount").Enabled= True |
Correct:
'*EnabletheDeleteAccountmenuitem. ActiveBar.Tools("DeleteAccount").Enabled= True |
9.8.3 Capitalize entire words to indicate their importance. To call attention to a word or words within a comment, use all uppercase letters . You can't apply formatting such as bold or italics because these features aren't supported by the Visual Basic code editor.
Correct:
PrivateSub UpdateDatabase() '*Purpose:Updatethedatabasestructure. '*DONOTUSEERRORTRAPPING!Lettheerrorscascadeupthe '*callstack. EndSub |
Comments are generally positioned in front of the code they document. To visually reinforce the relationship between a comment and the code it relates to, indent the comment at the same level as the code. Some developers indent code lines a single tab stop from the comment they follow, but if you were to remove the comments from a procedure that used this indentation scheme, it would quickly become apparent that the indentation does not correctly reflect the structure of the procedure. The code is not subordinate to the comment; it coexists with the comment.
Each procedure should have a comment header. Procedure comment headers can contain documentation of items such as the input parameters, the return values, the original author, the last person who edited the procedure, the last date revised, copyright information, or even a programmer's favorite color.
You have to decide what's important in a procedure comment header in your environment. At the very least, the comment header should contain the purpose of the procedure. The purpose should be stated clearly and concisely. If a procedure needs a thorough explanation, give it one, but avoid excessive wordiness, as in, "The purpose of this function is to " The Purpose heading itself tells the reader this much. A typical procedure comment header looks something like this:
PrivateFunction ShowPrintDialog() AsBoolean '*Purpose:DisplaythePrintdialogboxandgetprint optionsfromtheuser. EndFunction |
The next elements you should consider adding to your procedure comment header are the input (parameters) and output (return value) of the procedure. For example:
PublicFunction ConvertSQLtoCrystalFormat(strSQL AsString )_ AsString '*Purpose:ConvertastandardSQLstatementintoavalid '*selectionformulaforusewiththeCrystal '*printengine. '*Accepts:strSQL_avalidSQLstatement. '*Returns:ACrystalselectionformulathatisequivalent '*tothepassed-inSQLstring. EndFunction |
By including the purpose, accepted parameters, and return value comments in a procedure comment header, you create a much more understandable procedure. When you document the accepted parameters, be sure to note any special considerations or assumptions. For example, if the procedure expects a parameter to be formatted or within a certain range of values, include that information in the comments. Finally, if a procedure modifies any global data either directly or by changing the value of a parameter passed by reference, document this behavior in the procedure comment header as well.
All procedure comment headers should be formatted in the same way, and each piece of information should be clearly differentiated. The previous comment header has a highly recommended format; a reader can easily scan the header's components for the necessary information. The format is shown below as a shell, with no information. This format is used consistently throughout this book.
'*Purpose:xxx '*Accepts:yyy '*Returns:zzz |
Each heading ( Purpose , Accepts , or Returns ) is followed by two spaces (pressing the Tab key after typing the heading moves you to the proper location), a colon , another two spaces, and then the text for the heading. If multiple lines are required, you should indent subsequent lines to the start of the text after the colon, as shown below:
PublicFunction ConvertSQLtoCrystalFormat(strSQL AsString )_ AsString '*Purpose:ConvertastandardSQLstatementintoavalid '*selectionformulaforusewiththeCrystal '*printengine. '*Accepts:strSQL_avalidSQLstatement. '*Returns:ACrystalselectionformulathatisequivalent '*tothepassed-inSQLstring. EndFunction |
In addition to documenting the purpose, parameters, and return value of a procedure, you can also include the following elements in a procedure comment header:
NOTE
Although Event procedures are similar to the procedures that you actually write, you do not need to document the parameters of an Event procedure. You also don't need to document simple property procedures that encapsulate module-level variables . I have omitted the procedure comment header from some examples in this book when I've felt that it would convolute the topic being illustrated .
Every procedure should have a procedure comment header, and every header should contain at least the purpose of the procedure, the parameters accepted by the procedure, and any return value.
9.10.1 Document the purpose of a procedure in the procedure comment header.
Incorrect:
Function IsFormLoaded(strFormName AsString ) AsBoolean '*Thisfunctionacceptsaformnameandreturns '*TrueiftheformisloadedandFalseifitisnot. EndFunction |
Correct:
Function IsFormLoaded(strFormName AsString ) AsBoolean '*Purpose:Determinewhetheraspecifiedformisloaded. EndFunction |
9.10.2 Document the parameters of a procedure in the procedure comment header.
Incorrect:
Function IsFormLoaded(strFormName AsString ) AsBoolean '*Purpose:Determinewhetheraspecifiedformisloaded. '*Accepts:Thenameofaform. EndFunction |
Also incorrect:
Function IsFormLoaded(strFormName AsString ) AsBoolean '*Purpose:Determinewhetheraspecifiedformisloaded. '*Accepts:strFormName. EndFunction |
Correct:
Function IsFormLoaded(strFormName AsString ) AsBoolean '*Purpose:Determinewhetheraspecifiedformisloaded. '*Accepts:strFormName-thenameofaform. EndFunction |
9.10.3 Document the return value of a function in the procedure comment header.
Incorrect:
Function IsFormLoaded(strFormName AsString ) AsBoolean '*Purpose:Determinewhetheraspecifiedformisloaded. '*Accepts:strFormName-thenameofaform. '*Returns:TrueorFalse. EndFunction |
Also incorrect:
Function IsFormLoaded(strFormName AsString ) AsBoolean '*Purpose:Determinewhetheraspecifiedformisloaded. '*Accepts:strFormName-thenameofaform. '*Returns:Whetherornottheformisloaded. EndFunction |
Correct:
Function IsFormLoaded(strFormName AsString ) AsBoolean '*Purpose:Determinewhetheraspecifiedformisloaded. '*Accepts:strFormName-thenameofaform. '*Returns:Trueiftheformisloaded,Falseifnot. EndFunction |
The most common type of comment is generally referred to as an inline comment. While the procedure comment header documents the basics of the procedure, inline comments document the code itself. The implementation details aren't described in the procedure comment header because they might change over time and they add unnecessary complexity to the header. The place to document the implementation of a procedure is within the procedure itself. Take a look at the following procedure, which determines whether a form is loaded:
Function IsFormLoaded(strFormName AsString ) AsBoolean '*Purpose:Determinewhetheraspecifiedformisloaded. '*Accepts:strFormName-thenameofaform. '*Returns:Trueiftheformisloaded,Falseifnot. OnErrorGoTo PROC_ERR Dim intCounter AsInteger '*Sincereferringtoaformloadstheform,theproper '*waytodeterminewhethertheformisloadedistoloop '*throughtheformscollection,whichcontainsonly '*loadedforms. For intCounter=0 To Forms.Count_1 '*Ifthecurrentformisthespecifiedform, '*returnTrueandgetout. If Forms(intCounter).Name=strFormName Then IsFormLoaded= True GoTo PROC_EXIT EndIf Next intCounter '*Formwasnotfound;returnFalse. IsFormLoaded= False PROC_EXIT: ExitFunction PROC_ERR: MsgBox"basMainIsFormLoaded"&vbCrLf&"Error:"&_ Err.Number&vbCrLf&Err.Description ResumeNext EndFunction |
Notice how each decision is commented. As you read the code, the comments explain the implementation details. Try to place an explanatory inline comment at each construct, such as loops and decision structures. You should strive to make these comments clear and concise , but if something needs a detailed explanation, give it one. Since inline comments appear in the same location as the code they're describing, they're fairly easy to maintain. If you change the code, change the comment.
Inline comments are the most common and most important comments. Use them to document the implementation of procedures, walking the reader through the various twists and turns.
9.11.1 Place a comment before every If statement. If statements make decisions that affect the flow of execution. Document each If statement within your code.
Incorrect:
If Command$<>"" Then intLocation=InStr(Command$,strSearchString) If intLocation=0 Then '*Usethedefaultinifile,assumingit'sintheapplication '*path. strINIFile=App.Path&"\tpssuite.ini" Else '*Extractthenameofthedesignatedinifile. strINIFile=Mid$(Command$,Len(strSearchString)+intLocation) If Dir$(strINIFile)="" Then strINIFile=App.Path&"\tpssuite.ini" EndIf EndIf EndIf |
Correct:
'*Lookforcommandlineoptions. If Command$<>"" Then intLocation=InStr(Command$,strSearchString) '*Ifaniniparameterhasbeenfound,attempttouseit. If intLocation=0 Then '*Usethedefaultinifile,assumingit'sintheapplication '*path. strINIFile=App.Path&"\tpssuite.ini" Else '*Extractthenameofthedesignatedinifile. strINIFile=Mid$(Command$,Len(strSearchString)+intLocation) '*Ifthespecifiedfileisnotfound,use '*thedefaultinifile. If Dir$(strINIFile)="" Then strINIFile=App.Path&"\tpssuite.ini" EndIf EndIf EndIf |
9.11.2 Place a comment before every Select Case statement. Like If statements, Select Case statements evaluate expressions that affect the flow of execution. They are often more complex than If statements. You should thoroughly document Select Case statements.
Incorrect:
PrivateSub txtSearch_KeyDown(KeyCode AsInteger ,Shift AsInteger ) SelectCase KeyCode CaseIs =vbKeyPageDown '*Moveforwardinthelistthenumberofvisiblerows. datPhones.Recordset.MovegrdPhones.VisibleRows CaseIs =vbKeyPageUp '*Movebackwardinthelistthenumberofvisiblerows. datPhones.Recordset.Move-grdPhones.VisibleRows EndSelect EndSub |
Correct:
PrivateSub txtSearch_KeyDown(KeyCode AsInteger ,Shift AsInteger ) '*Iftheuserpressedanavigationkey,adjustthelist '*accordingly. SelectCase KeyCode CaseIs =vbKeyPageDown '*Moveforwardinthelistthenumberofvisiblerows. datPhones.Recordset.MovegrdPhones.VisibleRows CaseIs =vbKeyPageUp '*Movebackwardinthelistthenumberofvisiblerows. datPhones.Recordset.Move-grdPhones.VisibleRows EndSelect EndSub |
9.11.3 Place a comment before every loop, including For Next loops and Do loops. Every loop has a purpose, and often that purpose is not intuitively clear. Regardless of the complexity of the loop, document it with a comment preceding the loop.
Incorrect:
For intIndex=1 To lvwReleasedItems.ListItems.Count '*Gettheserialnumberfromthelistitem. strSerial=lvwReleasedItems.ListItems(intIndex).Text '*Deletetheserialnumberfromthetransfertable. strSQL="DELETE*FROMtblTransferSerials"&_ "WHERE[TransferNumber]="&_ m_lngTransferNumber&"AND[SerialNumber]="""&_ strSerial&""";" dbTransfers.ExecutestrSQL,dbFailOnError Next intIndex |
Correct:
'*Loopthroughtheselectedserialnumbers,andreleaseeachone. For intIndex=1 To lvwReleasedItems.ListItems.Count '*Gettheserialnumberfromthelistitem. strSerial=lvwReleasedItems.ListItems(intIndex).Text '*Deletetheserialnumberfromthetransfertable. strSQL="DELETE*FROMtblTransferSerials"&_ "WHERE[TransferNumber]="&_ m_lngTransferNumber&"AND[SerialNumber]="""&_ strSerial&""";" dbTransfers.ExecutestrSQL,dbFailOnError Next intIndex |
9.11.4 Place a comment before every statement in which a global variable is changed. As I discussed in Chapter 6, global variables are evil! However, if you absolutely need to use a global variable, document why you are changing it. This will make debugging a bit simpler.
Incorrect:
PrivateSub clsConnector_Terminate() '*Purpose:Keeptrackofthenumberofautomation '*clientsholdingreferencestothesuite. OnErrorGoTo PROC_ERR g_lngAutomationInstances=g_lngAutomationInstances-1 PROC_EXIT: ExitSub PROC_ERR: Call ShowError("clsConnector","Class_Terminate",Err.Number,_ Err.Description) GoTo PROC_EXIT EndSub |
Correct:
PrivateSub clsConnector_Terminate() '*Purpose:Keeptrackofthenumberofautomation '*clientsholdingreferencestothesuite. OnErrorGoTo PROC_ERR '*Decrementthecountofclientsholdingreferencestothis '*connectorobject. g_lngAutomationInstances=g_lngAutomationInstances-1 PROC_EXIT: ExitSub PROC_ERR: Call ShowError("clsConnector","Class_Terminate",Err.Number,_ Err.Description) GoTo PROC_EXIT EndSub |
Some developers use end-of-line comments, which appear at the end of a code statement and can extend for multiple lines. Try to use these comments for short descriptions only. If a longer description is necessary, use an inline comment instead. End-of-line comments were used more frequently in the past; most developers now choose (and rightly so) to use inline comments instead. The following is an example of an end-of-line comment:
DoWhile intLocation>0 '*Dowhileaspaceisfound. Loop |
End-of-line comments tend to make the code more difficult to read when they are used in constructs such as the code snippet above. However, a good use of an end-of-line comment is for documenting the declaration of a variable whose purpose might not be clear, as shown here:
Dim objTaxReporterAsObject '*MayholdanEmployeeobjectoran '*Employerobject. |
When you use multiple end-of-line comments (such as for multiple variable declarations at the top of a procedure), attempt to align them. This makes them a little easier to read.