Reddick .NET Conventions


Previous Topic Previous Next Topic Next
Xoc Software

Other Xoc managed sites:
http://grr.xoc.net
http://www.986faq.com
http://www.mayainfo.org
https://mayacalendar.xoc.net
http://www.yachtslog.com

These are a set of notes toward a set of .NET conventions. They are not ready for prime time, but keep checking back.

Version 0.90

Copyright © 2002-2003 by Gregory Reddick

Index:
Introduction
Summary

Introduction

What follows are the Reddick .NET Conventions. The objectives of the conventions are to make code:
  • More readable: Conventions allow a reader to understand the meaning of the code with less effort.
  • More maintainable: The code can be more reliably changed to fix bugs and enhance functionality.
  • More reliable: The code is more likely to perform as expected.
  • More efficient: The code performs faster or consumes fewer resources.
  • More consistent. Programmers can more easily read each other's code.

These conventions come from several sources. One source is the Reddick VBA Naming and Coding Conventions, that have been in use since 1992. Others come from the standards that Microsoft has put into place in the .NET Framework. Microsoft has implemented two tools that help with verifying that conventions have been met: FXCop and StyleCop. In a few places these conventions are slightly different than the rules that these tools implement, but in most places they concur. Xoc Software will also release a StyleCop library. Where possible, the FXCop or StyleCop rule that enforces the convention will be listed.

The current version of these conventions can always be found on the Xoc Software web site: http://www.xoc.net.

Internal Conventions

These conventions have some conventions. Since they apply to both Visual Basic .NET and C#, there are times where the conventions will differ slightly. These will be clearly marked. Examples will be preceded by [Visual Basic] or [C#] where there is differing syntax.

Casing

There are several different forms of casing that will be used throughout the conventions:

  • Pascal Case: Initial caps at the start of each word. Example: PascalCase
  • Camel Case: Initial caps at the start of each word except the first. Example: camelCase
  • Lower Case: All lower case. Example: lowercase
  • Hungarian: Initial type tag in lower case (with possible modifiers), followed by optional Pascal Case basename. Example: strHungarian

Documentation Rules [SA16xx]

Layout Rules [SA15xx]

Maintainability Rules [SA14xx]

Naming Rules [SA13xx]

Ordering Rules [SA12xx]

Readability Rules [SA11xx]

Spacing Rules [SA10xx]

Design Rules

Globalization Rules

Interoperability Rules

Mobility Rules

Naming Rules

Performance Rules

Portability Rules

Security Rules

Usage Rules

Name Spaces

Name Spaces should use Pascal Case. All namespaces for your code should be prefixed by a unique identifier for your organization, followed by a namespace for the application. For example, if your company is DotNetCo and your application is called Btriever, then the namespace for the application should be DotNetCo.Btriever. All code in a project should appear within one of your namespaces.

This matches the .NET Framework.

File Names

There are different naming rules for web applications than Windows applications.

WINDOWS APPLICATIONS: File names should use Pascal Case.

WEB APPLICATIONS: File names must be in Lower Case. If you use Visual Studio to create the file, you will need create the file using Pascal Case, then rename the file to Lower Case. This will make the capitalization of the file and the class within the file to come out correct. Exception: AssemblyInfo.cs, Global.asax, and Web.Config should be left in Pascal Case.

Web application files must be in lower case because many tools and search engines must deal with web sites created for other web servers than Internet Information Server (IIS). So if you create a web page named NamingConventions.Aspx, but two people type differing capitalizations into the address line of their web browser, then the server log file will record the two different capitalization. Log file analysis programs will typically show those as two separate files, rendering the statistics for those files meaningless.

Furthermore, one measure a search engine uses to weigh the value of the page is by the number of hyperlinks to it. If two other sites link to yours, one using NamingConventions.Aspx and the other using namingconventions.aspx, then the search engine may treat that as linking to two separate pages. This cuts the value of the page in half, causing it to get lower rankings in that search engine.

By always using lower case, a web page generally will be referenced using that case. This causes both the tools and search engines to properly keep track of the references to that file.

The AssemblyInfo.cs, Global.asax, and Web.Config files should be left in Pascal Case because these files are only accessed by the web server, not the web browser, and are never accessed through a hyperlink. Since the Visual Studio template creates them in Pascal Case, forcing people to rename them would lead to inconsistent casing of these files when that was neglected in some cases.

In general, each class should be placed into a separate file, however there are times where having a separate file for a small class becomes unwieldy. The filename should be the same as the class name that is in the file. If there are more than one class in the file, the file name be the same as the most important class in the file. If there are several small classes in the file of equal importance, then the file name should reflect the the group of classes.

Class Names

Class names should use Pascal Case. Since classes are representations of things, the names of classes should represent a noun. For example:

Employee
MayaDate
AppSettingsReader
This matches the .NET Framework.

Interface Names

Interface names should use Pascal Case. Interface names must begin with the letter I. Interface names should be a noun or adjective depending on whether the interface describes an object, such as IAccessControl, or modifies an existing object, such as IAccessible.

This matches the .NET Framework.

Method Names

Method names should use Pascal Case. Since methods are things that you do to an object, in otherwords, a verb, method names should usually start with a verb. For example:

AddMonths()
GetService()
InitializeLifetimeService()
This matches the .NET Framework.

Delegate Names

Delegate names should use Pascal Case.

Event Handler Names

Event Handler names should use Pascal Case. Event Handler names should always end with the suffix EventHandler. Event Handler names should use present or past tense to indicate whether the event is in the process of occuring, or has already occured. For example:

ButtonClickEventHandler
CheckChangeEventHandler
CheckChangedEventHandler
TickEventHandler
ValidatingEventHandler
ValidatedEventHandler
This matches the .NET Framework.

Event Argument Names

Event Arguments names should use Pascal Case. Event Argument names should inherit from System.EventArgs and end in the string EventArgs.

This matches the .NET Framework.

Enum Names

Enum names should use Pascal Case.

This matches the .NET Framework.

Variables

Variables should use Hungarian Case.

Fields and Properties

Fields and Properties names should use Hungarian Case. The base name of the Field or Property should be a noun. The value of the property describes the object, so should typically be an adjective. For example:

Color (values=Red, Blue, Green)
Size (value=400)
Text (value=Some random text)

Line Width

A physical line of code should be no more than 100 characters wide. A logical line longer than this should be broken into multiple physical lines.

This line width works well on a typical screen. It can be printed on a typical portrait 8 1/2" x 11" page by setting the left margin at .5" and the right margin to as small as possible. It also works on A4 paper with no margins. Or it can be printed on a landscape page at virtually any margins.

A suggestion to maintain this width is to place a comment at the top of each file of code. This comment should be a line of asterisks with a total of 100 characters in it, ending in a | symbol. For example:

[C#]

//**************************************************************************************************|

[Visual basic]

'***************************************************************************************************|

Then size the width of the code window so that the | symbol is just visible. A line of code should be broken into multiple physical lines when the code goes past the window edge.

In a few cases, long literal strings could be longer than 100 characters. The code should be reformulated to construct the string in code from shorter strings. For example:

strName = "A big, long, huge, extremely extremely long, long, long, long very long hugely long, enormously long string"

Should be broken up like this:

[C#]

strName = "A big, long, huge, extremely extremely long, long,"
    + "long, long very long hugely long, enormously long string";

[Visual Basic]

strName = "A big, long, huge, extremely extremely long, long," _
    & "long, long very long hugely long, enormously long string"

Always be careful to preserve spaces from the original string when breaking a string this way. In cases where breaking a string this way is impractical for performance reasons, an alternative such as loading the string from a resource should be considered. A long literal string in the argument to a function call should be placed into a constant, then the constant passed.

Value Data Types

I am currently accepting suggestions for tags for data types. I will definitely supply tags for these data types:

System.Boolean bool
System.Byte byte
System.Char char
System.Decimal dec
System.Double dbl
System.Int16
System.Int32 int
System.Int64 lng
System.IntPtr
System.Object obj
System.SByte
System.Single sng
System.String str
System.TypedReference
System.UInt16
System.UInt32
System.UInt64
System.UIntPtr

We may or may not supply tags for these data types:

System.ArgIterator
System.DateTime date? dtm?
System.Enum
System.Guid
System.TimeSpan
System.Void
System.Collections.DictionaryEntry
System.Collections.Specialized.BitVector32
System.Collections.Specialized.BitVector32.Section
System.Configuration.Assemblies.AssemblyHash
System.Data.SqlTypes.SqlBinary
System.Data.SqlTypes.SqlBoolean
System.Data.SqlTypes.SqlByte
System.Data.SqlTypes.SqlDateTime
System.Data.SqlTypes.SqlDecimal
System.Data.SqlTypes.SqlDouble
System.Data.SqlTypes.SqlGuid
System.Data.SqlTypes.SqlInt16
System.Data.SqlTypes.SqlInt32
System.Data.SqlTypes.SqlInt64
System.Data.SqlTypes.SqlMoney
System.Data.SqlTypes.SqlSingle
System.Data.SqlTypes.SqlString
System.Diagnostics.CounterSample
System.Diagnostics.SymbolStore.SymbolToken
System.Drawing.CharacterRange
System.Drawing.Color
System.Drawing.Point
System.Drawing.PointF
System.Drawing.Rectangle
System.Drawing.RectangleF
System.Drawing.Size
System.Drawing.SizeF
System.EnterpriseServices.BOID
System.EnterpriseServices.XACTTRANSINFO
System.IO.WaitForChangedResult
System.Reflection.Emit.EventToken
System.Reflection.Emit.FieldToken
System.Reflection.Emit.Label
System.Reflection.Emit.MethodToken
System.Reflection.Emit.OpCode
System.Reflection.Emit.ParameterToken
System.Reflection.Emit.PropertyToken
System.Reflection.Emit.SignatureToken
System.Reflection.Emit.StringToken
System.Reflection.Emit.TypeToken
System.Reflection.InterfaceMapping
System.Reflection.ParameterModifier
System.Runtime.InteropServices.ArrayWithOffset
System.Runtime.InteropServices.BIND_OPTS
System.Runtime.InteropServices.BINDPTR
System.Runtime.InteropServices.CONNECTDATA
System.Runtime.InteropServices.DISPPARAMS
System.Runtime.InteropServices.ELEMDESC
System.Runtime.InteropServices.ELEMDESC.DESCUNION
System.Runtime.InteropServices.EXCEPINFO
System.Runtime.InteropServices.FILETIME
System.Runtime.InteropServices.FUNCDESC
System.Runtime.InteropServices.GCHandle
System.Runtime.InteropServices.HandleRef
System.Runtime.InteropServices.IDLDESC
System.Runtime.InteropServices.PARAMDESC
System.Runtime.InteropServices.STATSTG
System.Runtime.InteropServices.TYPEATTR
System.Runtime.InteropServices.TYPEDESC
System.Runtime.InteropServices.TYPELIBATTR
System.Runtime.InteropServices.VARDESC
System.Runtime.InteropServices.VARDESC.DESCUNION
System.Runtime.Serialization.SerializationEntry
System.Runtime.Serialization.StreamingContext
System.RuntimeArgumentHandle
System.RuntimeFieldHandle
System.RuntimeMethodHandle
System.RuntimeTypeHandle
System.Security.Cryptography.DSAParameters
System.Security.Cryptography.RSAParameters
System.Threading.LockCookie
System.Threading.NativeOverlapped
System.Web.UI.WebControls.FontUnit
System.Web.UI.WebControls.Unit
System.Windows.Forms.BindingMemberInfo
System.Windows.Forms.DataGridCell
System.Windows.Forms.LinkArea
System.Windows.Forms.Message

Parenthesis

Parentheses should have no extraneous white space around them. For example:

[C#]
ATestFunction(intParam1, intParam2);

[Visual Basic]
ATestFunction(intParam1, intParam2)

not

[C#]
ATestFunction ( intParam1, intParam2 ); //WRONG

[Visual Basic]
ATestFunction ( intParam1, intParam2 ) 'WRONG

C# Only: The exception is when using control structures that are followed by a value in parenthesese, such as if or while. In these cases, a space should preceed the first parenthesis. For example:

if (intValue == 1)
    System.Console.WriteLine("Value == 1");

not

if(intValue == 1)
    System.Console.WriteLine("Value == 1");
This produces the easiest to read code.

Commas should have no extraneous white space before them, and one space after them. For example:

[C#]
ATestFunction(intParam1, intParam2, intParam3);

[Visual Basic]
ATestFunction(intParam1, intParam2, intParam3)

not

[C#]
ATestFunction(intParam1,intParam2 ,intParam3); //WRONG

[Visual Basic]
ATestFunction(intParam1,intParam2 ,intParam3); //WRONG
This produces the easiest to read code.

Namespaces

Every class should be in an explicitly named namespace. Namespaces should be constructed using the following format:

<company/entity identifier>.<component identifier>[.<subcomponent identifiers>]

The company/entity identifier should be a short word or abbreviation that identifies the company or other entity producing the software. Examples:

Company/Entity Identifier
Rdnc Software Rdnc
Microsoft Corporation Microsoft
University of Puget Sound Ups

Component identifier is a short description of the component the namespace is describing. Examples:

Component Identifier
Maya Calendar Mayacal
SQL Server SqlServer
Enrollment Application Enroll

You can then break down the Component into subcomponent namespaces to as many levels deep as necessary.

Examples:

Rdnc.Mayacal.Engine
Rdnc.Mayacal.UserInterface
Microsoft.VisualBasic
Ups.Enroll
This matches the style of the hierarchy of the .NET Framework.

In an ASP.NET application, each subdirectory should have its own namespace. For a company named RDNC and a website named Mayacal, the table below shows the directory and the namespace that files in that directory should fall in.

Directory Namespace
/ Rdnc.Mayacal
/admin Rdnc.Mayacal.Admin
/admin/cleanup Rdnc.Mayacal.Admin.Cleanup
/admin/browse Rdnc.Mayacal.Admin.Browse
/help Rdnc.Mayacal.Help
This allows the class names of pages in different directories to avoid conflicting with each other. In particular, the default.aspx page in each directory by default is given the class name _default. Two different directories with a _default class won't conflict with each other because they will be in different namespaces.

Globals

Every project should included a file called Globals.vb or Globals.cs. This file should contain a class called Globals. This class should contain the Main procedure that starts the program for Windows applications. You will have to move the Main procedure from some other file if you used a Visual Studio template to create the project.

The Globals class can also contain static helper variables and procedures used throughout the rest of the assembly.

There should be a global place for generic code inside the project. The entry point for the project should reside here.

Custom Exceptions

If the application throws an exception, it should never any exception from the .NET Framework. Instead it should create its own custom exceptions and throw those. All custom exceptions should inherit from ApplicationException and be placed into a file called Exceptions.vb or Exceptions.cs. For example:

Public Class InvalidFileException
   Inherits System.ApplicationException
   Sub New(ByVal strMessage As String)
      MyBase.New(strMessage)
   End Sub
End Class
or
public class InvalidFileException: System.ApplicationException
{
    public InvalidFileException(string strMessage): base(strmessage)
    {
    }
}
Application Exceptions should be individually marked.

Simple Types

In VB.NET and C#, the simple types that are integrated in the language map to data types supplied by the .NET Framework. When declaring variables of these simple types, you should use the keyword supplied by the language rather than the .NET Framework type.

.NET Framework type C# Alias Visual Basic Alias
System.Boolean bool Boolean
System.Byte byte Byte
System.Char char Char
System.DateTime System.DateTime Date
System.Decimal decimal Decimal
System.Double double Double
System.Single float Single
System.Int32 int Integer
System.Int64 long Long
System.Object object Object
System.SByte sbyte System.SByte
System.Int16 short Short
System.String string String
System.UInt32 uint System.UInt32
System.UInt64 ulong System.UInt64
System.UInt16 ushort System.UInt16

Examples:

[C#]
int intValue;
long lngFoo;
uint uintBar;
System.DateTime dtmToday;

[Visual Basic]
Dim intValue As Integer
Dim lngFoo As Long
Dim uintBar As System.UInt32
Dim dtmToday As Date

{Alternative Standard: None of the language integrated data types may be used. Instead only the .NET Framework data types should be used.}

Using the .NET Framework data types only also works. There is a slight bias toward using the language integrated types since most code examples will appear that way.

Collections

The names of collections should be the plural of one of the items in the collection. So if you have a class that represents a collection of Employee objects, the name of the collections should be Employees. Be careful when naming classes that will later be put into a collection. You should name the class that describes the specifics of a book Detail, not Details, if you are later going to create a collection of these Detail objects. Or better might be to rename the class altogether, as BookDescription.

Properties that return a collection should be named the same as the collection that they return. For example, a property that returns the Employees collection should be named Employees.

Comment Blocks

Every non-private piece of code should have an XML comment that describes the feature. For more details see the documention of XML Comments. Private code, internal only to the class in which it is defined, is not strictly required to have XML Comments, but is recommended. Although the Visual Basic compiler doesn't parse XML Comments in the current version, you should still add the comments to the source code. The format to use for Visual Basic XML comments is:

'/<summary>
'/This is the summary
'/</summary>

Operators

Binary operators, such as +, should have a space placed on either side of them. Unary operators should have no extra space between the operator and the thing that they operate on. For example:

intFoo + intBar
intFoo ^ intBar
intFoo++
intFoo * -6

Imports and Using Statements

A Imports statement in Visual Basic and a using statement in C# allows the programmer to avoid having to include the complete namespace before a class name. Imports and using statements, however, are at the expense of explicitness in the code. They can also cause ambiguity of class names if there are two classes with the same name in two different namespaces. (The compiler will complain if there is any ambiguity.)

It is never wrong to include the complete namespace before a class name. However, if the complete namespace is used one place in a file, then it should always be used in that file. Strive to be consistent within a particular file.

If there is more than one Imports or using statement in a file, then they should be alphabetized. For example:

[Visual Basic]
Imports System
Imports System.ComponentModel
Imports System.Diagnostics
Imports System.Drawing
Imports System.IO
Imports System.Web
Imports System.Windows.Forms
Imports System.Xml
Imports Xoc.Header

[C#]
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Web;
using System.Windows.Forms;
using System.Xml;
using Xoc.Header;

Imports and using statements that, due to code maintenance, are no longer in use should be removed. After a major rewrite, it is recommended to comment out all the Imports or using statements and recompile. Any that are complained about by the compiler should be commented back in, until the program compiles. Any that are still commented out at that point should be removed.

In Visual Basic, project level imports can be specified in the Project Properties dialog. These should not be used, and any default ones should be removed.

It is easier to see if you have a namespace already defined if it is alphabetical order.

Project level Imports statements in Visual Basic prevent the programmer from just looking at the top of the file to see what namespaces a given class comes from. He must look at both the top of the file and the global Imports to find the imported namespaces. It also increases the likelyhood that a class name will conflict with a class in another namespace.

{Alternative Standard: Imports and using statements should not be used.}

Declarations

Each variable should be declared on its own line. For example:

[Visual Basic]
Dim intValue As Integer
Dim intAnother As Integer
Dim strResult As String

[C#]
int intValue;
int intAnother;
string strResult;
not
[Visual Basic]
Dim intValue, intAnother As Integer, strResult As String 'Wrong

[C#]
integer intValue, intAnother; //Wrong
Using one declaration per line makes the definition of the variable clear. In
Dim intValue, intAnother As Integer
it is unclear what the definition of intValue is. In Visual Basic 6.0 and before, intValue would be a variant data type. It makes finding the variable easier, since the variable declaration will always be near the start of a line. It makes maintenance easier, since a variable declaration can be removed by simply cutting a line. In the standard keyboard profile for Visual Studio, a line can be cut using Ctrl+L when the cursor is anywhere within the line, rather than having to select from within the line with the mouse.

Comments

A comment is placed into the code to help clarify what the intention of the code is. Each comment may be placed onto a line by itself or at the end of an existing line of code. A comment at the end of a line of code should be use in only a few places:

  • The end of a variable declaration line.

All other comments should be placed on a separate line above the line they are documenting and indented to the same level. A comment of this sort is generally preceded by a blank line unless it is the first line of an indented block. For example: vt = vti.VarType 'Special hack for analyzing my code If LCase$(Left$(strParamName, Len(strcDecPrefix))) = strcDecPrefix Then strDataType = strDecimal End If If it is the first line of an indented block, it is not preceded by a blank line. For example: If mboolShowProperties Then 'Show properties for each member For Each mi In ci.Members Comments should state the intention of the code not how it performs the task. This is an example of a worthless comment: 'Place the VarType into the vt variable vt = vti.VarType It is worse than no comment at all. The comment is wrong if the code changes to use the variable name vtCur instead of vt without changing the comment. When reading a comment that doesn't match the code, the question becomes whether the comment is correct or the code is correct. Usually it is the comment that is wrong, but it may take some time to prove that. In general, do not write comments that have to be maintained, because in the real-world people frequently do not maintain comments. A comment that states the intention of the code, though, may be useful. For example: 'Store VarType for recovery in error condition. vt = vti.VarType However, use these comments only when the intention is not immediately clear when reading the code. Instead strive to make the code self explanatory, through good naming and coding conventions.

Methods

Methods must always be followed by arguments in parentheses. If the method takes no arguments, it must be followed by ().

In Visual Basic .NET, it is optional to have a method that takes no arguments be followed by parentheses. However, this makes it difficult to differentiate between a method and a property when reading the code. In C#, the compiler enforces this rule.

Assertions

Indenting

Deprecated Features

Runtime Error Handling

AssemblyInfo

Attributes

Attributes should be placed on a line by themselves preceding the thing they modify. If there is more than one attribute, they should each be surrounded by their delimiters (<> in Visual Basic .NET, and [] in C#), rather than using a comma separated list of the attributes. Attributes should be listed in alphabetical order. Attributes that take no arguments should be followed by (). For example:

[C#]
	[CLSCompliant(false)]
	[ComImport()]
	[Guid("000214F2-0000-0000-C000-000000000046")]
	[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
	public interface IEnumIDList
	...
[Visual Basic]
	<CLSCompliant(false)> _
	<ComImport()> _
	<Guid("000214F2-0000-0000-C000-000000000046")> _
	<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
	public interface IEnumIDList
	...
Being on a separate line and being individually marked makes it easier to read and maintain.

Summary

The Reddick .NET Conventions make your code easier to read, easier to maintain, more reliable, and faster.


Top