22. VB.NET and C#
While coding in VB.NET and C#, an additional set of Coding Standards is used. Obviously the rules above that only apply to the C language do not apply to VB.NET, but others, like naming conventions for variables and function names apply. Also there are a handful of rules that only apply to VB.NET as it is a .NET language:
Variable and function/command names (in VB commands are called Subroutines) largely follow the C rules for naming of variables and functions. The SCOPE that C has that VB.NET doesn’t have is
static
, so the ‘s’ scope prefix of course is not used.We do not use Modules unless we have to. Someone who knows standard O-O design will probably never need a Module. A module is like a class in which every member is both
Public
andShared
(meaning all members are accessed without instantiating the class). While I suppose it is POSSIBLE this might be needed somewhere, I have yet to encounter such a case, and since the same thing can be achieved with a class, there really shouldn’t ever be a real need for a Module. (We occasionally have to use a VB Module if we are modifying already-existing code which has a significant portion of it in a VB Module.)Class Names, so far there are no evolved hard-and-fast rules. So far, class names have been named like controls on a form, e.g. frmRearSusp.vb. Renaming classes after they have been created is very easy (right click class in project window and select rename [or
Ctrl-R Ctrl-R
], and all related code is automatically modified).I’ve been toying with using the Eiffel standard of all caps with words separated by underscores, but this is not in keeping with ANY VB.NET code I have ever seen (not that it needs to be).
Access modifiers are always specified for class features (code and data). Because the “Private” access modifier violates normal O-O rules as defined in [Meyer1997] (it is only accessible by the immediate class definition, and not by heir classes, nor is it redefinable by heir classes), we do not use it at all. Instead we only use
Public
orProtected
access modifiers. If an access modifier is omitted, I believe its default access modifier isPrivate
(which is the case if a class variable is just defined withDim
[dimension] — a BASIC language keyword since day one to declare/define variables). However,Dim
is always used for function/command local variables.Properties: properties provide (to client code) the “appearance” of access to a public data member, but in fact there is a code interface by way of a “Set” routine and a “Get” function. Up to this writing, I have generally simply provide
Public
variables and use coding discipline NOT to write directly to these variables via client code, but instead provide a routine to do that instead. But I find nothing wrong with using Properties if convenient to do so. Properties provide syntax like this:other_obj.ifSpeedInKmh = lfComputedSpeed
while in fact providing and enforcing data encapsulation, where
ifSpeedInKmh
is in fact a property and the variable (if there is one) that stores its data is hidden from client code.Variable Basics:
Like in Eiffel, almost all variables are references to an object. The exception (like Eiffel) are data types that can be handled directly by the CPU(s):
various types of signed and unsigned integers
single-precision floating point (in VB called
Single
)double-precision floating point (in VB called
Double
)decimal (while this may or may not be handled directly by the CPU, I believe this one is also normally a “flat” class and not an object reference).
In 12 years I have YET to have to convert a flat object to a reference object or vice versa, but like C#, the ability to do so is available if needed.
Function & Subroutine Arguments
Unlike Eiffel, flat-vs-object-reference can be converted on the fly during a function/subroutine call. Normally we don’t want that to happen since usually the CPU overhead required to perform such a conversion is unnecessary. However, we DO like to specify this simply to be explicit in VB.NET syntax: the keywords
ByRef
andByVal
provide explicit syntactical specification of what the function/routine expects: flat classes are passedByVal
and object references are normally passedByRef
so as to avoid the conversion.However, there is one instance where you want the function/subroutine to have a COPY of the referenced object rather than direct access to it, in which case the function/subroutine is defined with
ByVal
prefixing that particular argument, after which the compiler enforces that the function/subroutine always has a COPY and not a REFERENCE.We do not overload whenever possible. For the same reason that Eiffel does not support overloading, we do not use Overloading even though it is available. It is almost always clearer (making code easier to understand) to have different function/subroutine names for routines that provide similar operations but have different signatures.
Events: while the .NET syntax for providing agents is available and can be used (the ‘+’ symbol, similar to adding agent calls in Eiffel to a windows event, e.g. Mouse Click), we typically simply use a regular function name and use the keyword
Handles
like this:... Handles event_name
in the signature of the function/subroutine definition.
When this is not possible, the .NET syntax is used.
We normally do not use “Shadows” since it violates standard O-O rules. Using
Shadows
as a feature modifier enables a function/subroutine to have the same name as an inherited feature, but it does not redefine it, but rather hides the parent feature. This MIGHT be the only way to handle when such a feature needed to be redefined and could not be (since we didn’t have access to its source code). However, that name WOULD NOT be usable in a polymorphism situation: it would NOT stand for the parent class’s feature of the same name. In my opinion, this is an abomination of standard O-O rules, and was probably a work-around for some other problem created by also not following standard O-O rules in some other part of the language.Namespace Names
As with other naming guidelines, the goal when naming namespaces is creating sufficient clarity for the programmer using the library to immediately know what the content of the namespace is likely to be. The following template specifies the general rule for naming namespaces:
<Company>.(<Product>|<Technology>)[.<Feature>][.<Subnamespace>]
The following are examples:
Fabrikam.Math
Litware.Security
HM.Dash.Logging
The following is advice from Microsoft on namespace names.
DO prefix namespace names with a company name to prevent namespaces from different companies from having the same name.
DO use a stable, version-independent product name or technology category name at the second level of a namespace name.
DO NOT use organizational hierarchies as the basis for names in namespace hierarchies, because group names within corporations tend to be short-lived. Organize the hierarchy of namespaces around groups of related technologies.
DO use PascalCasing, and separate namespace components with periods (e.g., Microsoft.Office.PowerPoint). If your brand employs nontraditional casing, you should follow the casing defined by your brand, even if it deviates from normal namespace casing.
CONSIDER using plural namespace names where appropriate.
For example, use System.Collections instead of System.Collection. Brand names and acronyms are exceptions to this rule, however. For example, use System.IO instead of System.IOs.
DO NOT use the same name for a namespace and a type in that namespace.
For example, do not use Debug as a namespace name and then also provide a class named Debug in the same namespace. Several compilers require such types to be fully qualified.
22.1. Namespaces and Type Name Conflicts
DO NOT introduce generic type names such as Element, Node, Log, and Message.
There is a very high probability that doing so will lead to type name conflicts in common scenarios. You should qualify the generic type names (FormElement, XmlNode, EventLog, SoapMessage).
There are specific guidelines for avoiding type name conflicts for different categories of namespaces.
Application model namespaces
Namespaces belonging to a single application model are very often used together, but they are almost never used with namespaces of other application models. For example, the System.Windows.Forms namespace is very rarely used together with the System.Web.UI namespace. The following is a list of well-known application model namespace groups:
System.Windows* System.Web.UI*
DO NOT give the same name to types in namespaces within a single application model.
For example, do not add a type named Page to the System.Web.UI.Adapters namespace, because the System.Web.UI namespace already contains a type named Page.
Infrastructure namespaces
This group contains namespaces that are rarely imported during development of common applications. For example, .Design namespaces are mainly used when developing programming tools. Avoiding conflicts with types in these namespaces is not critical.
Core namespaces
Core namespaces include all System namespaces, excluding namespaces of the application models and the Infrastructure namespaces. Core namespaces include, among others, System, System.IO, System.Xml, and System.Net.
DO NOT give types names that would conflict with any type in the Core namespaces.
For example, never use Stream as a type name. It would conflict with System.IO.Stream, a very commonly used type.
Technology namespace groups
This category includes all namespaces with the same first two namespace nodes (<Company>.<Technology>*), such as Microsoft.Build.Utilities and Microsoft.Build.Tasks. It is important that types belonging to a single technology do not conflict with each other.
DO NOT assign type names that would conflict with other types within a single technology.
DO NOT introduce type name conflicts between types in technology namespaces and an application model namespace (unless the technology is not intended to be used with the application model).