Coding Conventions
The topic of coding conventions (or coding standards) comes up a lot. You can always find it as a recent topic in the microsoft.public.dotnet.languages.vb newsgroup. A search on Google for “Coding Conventions” returns 170,000 hits. Why such interest? I think people simply want to know the way to format code.
With that said, there is no single way of authoring Microsoft Visual Basic code that is universally agreed upon. Microsoft defines a set of coding conventions that are adopted to a large or small degree by many development teams, but it’s very common for different sets of developers to follow, at least slightly, different rules.
Why Are Coding Conventions Important?
You might wonder why we have conventions at all if nobody can agree on them. The reason, in a word, is consistency. At a minimum, code should be consistent with itself. This means that if you have multiple developers working on a given application, they should all be following the same rules. This can happen only if the rules are documented and agreed upon.
Ideally, all the code in an organization is consistent—although in practical terms, this virtually never happens. If the code that’s currently being written is consistent with code that’s developed in other departments, developers are more portable within the organization.
Coding conventions also affect code maintenance. Over the lifetime of a given application, more resources will be used to enhance and maintain the application than were originally used to develop the application. Also, applications are not typically maintained exclusively by the original authors. If the application uses consistent coding conventions, it’s easier to decipher and modify. For these reasons alone, coding conventions have value.
The true value of coding conventions is that they convey additional information not by what is written, but by how it’s written. Based on a given set of coding conventions, you can tell certain things by looking at the following statements:
What could you tell about this code? Based on the conventions that will be outlined in this chapter, you would know that currentUser is a private or protected variable and not a public property. You would know that IsValid, on the other hand, is a public property that returns a Boolean value. You would also know that txtUserIsValid is a user-interface element, and more specifically, a TextBox. Compare this case with the following code:
CurrentUser=NextUser() IfCurrentUser.CheckValidThen UserIsValid.Text= "Yes" EndIf
In this case, you can’t make any assumptions. There’s no way to tell whether CurrentUser is a public property or an internal variable. You know that UserIsValid is some kind of object, but is it a user-interface object? There’s no way to be certain, as any class could expose a Text method. If you do assume that it’s a user-interface element, you don’t know whether it’s a Label, a TextBox, or something else. Left to maintain this code, you would have a certain amount of spelunking to do just to figure out what you’re looking at.
I want to repeat that coding conventions described in this book are not the only ones that work. You could have standards where _currentUser, currentUser, current_user, sCurrentUser, and CURRENT_USER would all denote completely different things. The point is simply to have standards because doing so will enhance the readability and maintainability of your code.
How Much Is Too Much?
Once you’ve decided that you’re going to institute coding conventions, you can either define a few guidelines or attempt to define a rule for nearly every conceivable coding scenario. If you want a truly minimalist approach, just remember, “It’s more important to be consistent than to be right.” This philosophy has a number of ramifications. It means that if you’re maintaining existing code, you simply follow the coding conventions used by the original author. Also remember that it’s never a good idea to fix the coding conventions of an entire application, unless the application is already being rewritten for some other reason.
Coding Recommendations
If you want something a little more structured, I’ll offer what I consider to be the most important conventions for Microsoft Visual Basic .NET.
Option Strict On
Option Strict On was used for all the samples in this book simply because it results in safer, more reliable, often better performing, and more explicit code. Almost all production code should be authored with Option Strict On. There is one exception: if you’re specifically trying to leverage the late-binding feature of Visual Basic .NET, it’s OK to use Option Strict Off. However, this code should be contained within its own file, and the majority of the application should still use Option Strict On.
Camel Casing
Camel casing means the first letter is a lowercase letter and then the beginning of each subsequent word is an uppercase letter. For example: currentUser, firstCustomer, and myDocumentsFolder. You should use camel casing for parameters passed to methods, for local variables, and for private or protected class variables. Using this convention makes it easy to see that something is an internal variable and not externally accessible.
Pascal Casing
Pascal casing means that you capitalize the first letter of every word. For example: GetCustomer, SaveChanges, and FirstName. Pascal casing should be used for all classes, enumerations, methods, properties, public fields, namespaces, etc. In other words, Pascal casing is used for everything except the few cases where you use camel casing.
This use of Pascal and camel casing is consistent with all the classes you’ll find in the base class library of the .NET Framework.
Comments
You should comment every procedure. This commenting should include at a minimum the purpose of the method, a description of the arguments, and the meaning of the value returned. You should also comment all variables and properties, and all logical blocks of code. The most important rule of commenting is that the comments should describe the code, not repeat the code. Consider the following two sections of code:
'openatextfile DimsrAsNewStreamReader("c:somefile.txt") 'declareaninteger DimiAsInteger 'looponreadingeachline Whilesr.ReadLine() 'incrementi i+=1 EndWhile 'Countthelinesinafile DimsrAsNewStreamReader("c:somefile.txt") DimiAsInteger Whilesr.ReadLine() i+=1 EndWhile
Which is more informative? The first example comments every line. However, the comments don’t tell you anything you don’t already know. They’re just repeating the code. Sometimes, less is more. In the second example, the comment explains what the code does, not how it does it. This is the purpose of comments. If you feel you have to comment how the code is doing something, perhaps because a certain code block is extremely complex, consider rewriting the code to simplify it. Remember, comments are designed to give additional information to a programmer who already has the ability to read code.
Do Things the Visual Basic Way
When dimensioning a variable, it’s better to say Dim cn As New SqlConnection() than to say Dim cn As SqlConnection = New SqlConnection(). This approach also means it’s preferable to use Len instead of String.Length, MsgBox instead of MessageBox.Show, and Declare instead of DllImport. It’s also worth noting that conversion functions such as CInt and CBool are not really functions; they are language keywords and are actually faster than CType(x, Integer).
There is a great misunderstanding about the intrinsic Visual Basic methods. People have the impression that if you use Len, you’re not writing “pure” .NET code. This is completely untrue. Len is simply a method in the Microsoft.VisualBasic namespace. This namespace is part of the core Framework. By using these methods, you aren’t burdening your application with carrying around something equivalent to the Visual Basic 6 runtime. The exception to this is the Microsoft.VisualBasic.Compatability namespace. This exists only for migrating Visual Basic 6 code to Visual Basic .NET, and it should never be used for new development.
Use Meaningful and Consistent Names
You should pick names that are readable and mean something. You should use variables such as i, j, and k only for trivial loops. If you have a property or variable that stores a Boolean value, its name should contain Is to denote a True or False value or yes/no—for example, IsValid or IsAuthorized.
Constants and Enumerations
Your code should never contain magic numbers or strings. You should use constants and enumerations instead. For example:
'Bad DimbufferSize(1024*1024)AsByte 'Good ConstK_BYTEAsInteger=1024 ConstMEG_BYTEAsInteger=1024*K_BYTE DimbufferSize(MEG_BYTE)AsByte
In the first example, who knows what the code means? In the second example, it’s obvious that the code is creating a 1-megabyte buffer. The only raw numbers that should appear in your code are 0 and 1. Anything else should be defined as a constant or enumeration. It is also a matter of convention to declare constants in all capital letters with an underscore separating each word.
Enumerations can also make your code more readable and maintainable. Compare the following:
PublicEnumPriority Low Medium High EndEnum 'Bad Mail.Priority=1 'Good Mail.Priority=Priority.Medium
Using the enumeration obviously makes the code more readable. Enumerations in .NET have the added advantage of allowing you to easily convert them to strings. For example: lblPriority.Text = Mail.Priority.ToString().
Hungarian Notation
When you use Hungarian notation, you prefix each variable declaration with mnemonics that denote the type. For example, m_sUserName indicates a private or protected member variable of the String data type. In Visual Basic 6, this type of Hungarian notation was sometimes used. What was almost universal, however, was to prefix all user-interface elements with a three-character prefix: txt for TextBox, lbl for Label, and so on.
In Visual Basic.NET, m_ should be used only for private or protected members that are accessed through a public property procedure. Other protected member variables should simply be given a camel-case name. Primitive data types should not be prefixed, so you should simply use customerName, and not sCustomerName or strCustomerName.
Microsoft originally recommended against using any form of Hungarian notation; however, the prefixes for user-interface elements are so ingrained and useful that they’re now accepted. If you’re building an application that has a user name label, text box, and member variable, you can run out of creative names quickly. However, with prefixes for the user interface elements, it’s obvious what lblUserName, txtUserName, and userName are in your code.
Table 15-1 shows the recommended prefixes for user-interface elements:
Class |
Prefix |
---|---|
Button |
btn |
CheckBox |
chk |
CheckedListBox |
clst |
ColorDialog |
cdlg |
ColumnHeader |
chdr |
ComboBox |
cbo |
ContextMenu |
cmnu |
CrystalReportViewer |
crv |
DataGrid |
grd |
DateTimePicker |
dtp |
DomainUpDown |
dud |
ErrorProvider |
erp |
FontDialog |
fdlg |
Form |
frm |
GroupBox |
grp |
HelpProvider |
hlp |
HScrollBar |
hsb |
ImageList |
img |
Label |
lbl |
LinkLabel |
lnk |
ListBox |
lst |
ListView |
lvw |
Menu |
mnu |
MonthCalendar |
cal |
NotifyIcon |
nico |
NumericUpDown |
nud |
OpenFileDialog |
odlg |
PageSetupDialog |
psd |
PictureBox |
pic |
PrintDialog |
pdlg |
PrintDocument |
pdoc |
PrintPreviewControl |
ppc |
PrintPreviewDialog |
ppd |
ProgressBar |
pbr |
RadioButton |
rad |
RadioButtonList |
rbl |
RichTextBox |
rtf |
SaveFileDialog |
sdlg |
Splitter |
spl |
StatusBar |
sbr |
StatusBarPanel |
sbr |
TabControl |
tab |
TabPage |
pge |
TextBox |
txt |
Timer |
tmr |
ToolBar |
tbr |
ToolBarButton |
tbb |
ToolTip |
tip |
TrackBar |
trk |
TreeNode |
nod |
TreeView |
tvw |
VScrollBar |
vsb |
Because data objects are so common, they’re also sometimes prefixed. If you decide to prefix data objects, you should use the conventions listed in Table 15-2.
Class |
Prefix |
---|---|
Connection |
cnn |
Command |
cmd |
CommandBuilder |
cb |
DataAdapter |
da |
DataColumn |
dcl |
DataReader |
dr |
DataRow |
drw |
DataSet |
ds |
DataTable |
dt |
DataView |
dv |
Use Good Object-Oriented (OO) Practices
Visual Basic 6 is an event-driven programming language. If you have a structured programming background, you know that in Visual Basic 6 you simply had to think differently to solve the same problems.
In addition to being event driven, Visual Basic .NET is fully object oriented. Guess what? This means thinking differently to solve the same problems. The purpose of this book isn’t to teach the concepts of object-oriented (OO) languages. Many great titles exist that cover the concepts quite adequately. The point I want to make is that you no longer have a choice about doing or not doing OO. All Visual Basic .NET code is OO by nature. When you create a Web page or a form, it shows up in your code as a class. The Framework itself makes extensive use of inheritance, polymorphism, and other OO concepts. In addition, the Framework was specifically designed to assume that you would inherit portions of it and implement interfaces as the way to build solutions.
Visual Basic 6 was not OO, and you might not be used to thinking this way. I strongly recommend learning object-oriented analysis and design practices and using them in your applications.
Some Things Were Bad, Are Bad, and Always Will Be Bad
Goto, Option Explicit Off, and On Error Resume Next have generally never been good ideas. They still aren’t. But, the list of “bad things” has actually grown with Visual Basic .NET. You should now avoid the On Error construct completely. Instead, you should use structured error handling in your code:
Try DimxAsInteger x=x/0 CatchexAsException MsgBox(ex.Message) Finally 'Thiscodealwaysruns EndTry
When handling errors this way, you wrap the code that might fail in a Try block. If an exception occurs, execution will jump to the Catch block, where the error can be dealt with appropriately. Code in the Finally block will always run, whether an exception occurs or not. For a detailed description of structured exception handling, see the related sample. It suffices to say that structured exception handling should be used in place of On Error, in every case.
You should also avoid type characters when declaring variables. So instead of using
Dims$
you should use
DimsasString
One exception to the “has always been bad” rule is variable declaration. You used to pay a performance penalty if you dimensioned variables using As New:
DimcnnAsNewSqlConnection()
This is now the recommended mechanism for instantiating objects.
User Interface
You can do some simple things to make your application more accessible. The easiest thing to do is to add accelerators for all menus, labels, buttons, and so forth. Each application should also contain an About form on the Help menu that at a minimum explains where the user can get support.
When naming user-interface elements, it’s not a crime to have Label1 and Panel1, unless those objects will be accessed by code. Any object that is accessed by code—either to access properties or methods, or handle events— should have a meaningful name.
Conclusion
Coding conventions are all about making code that’s more readable and ultimately more maintainable. You can almost think of conventions as metadata for your code, as they convey additional information beyond the code itself. While this section has outlined many common and agreed-upon coding conventions for Visual Basic .NET, you might need additional or different conventions within your organization. That’s fine. What is critical is that within your organization you adopt conventions that everyone agrees to. Conventions are simply a mechanism for consistency.