Coding Guidelines
Programming is as much science as it is art. Code written by a person is a reflection of their logical and creative side, in how it's laid out, the algorithms it implements, and how it visually flows. It is because of this artistic element that debates on code style are just as personal and religious as licensing arguments. Some aspects can be lent to logic arguments - reducing bugs or other common developer defects. But many others are solely for making a consistent, organized visual appearance.
The following code style has been designed by the Ensemble OS Project, based on prevailing de facto industry code style standards for C#. We urge you to use this style in your code, and require its adherance for code accepted into the mainline trunks. The style is based off the following other styles:
- Brad Abrams Internal Coding Guidelines
- Philips Medical Systems C# Standard
- Mike Kruger / ICSharpCode Standard
- IDesign C# Coding Standard
For Visual Studio users, it is highly recommended to use the JetBrains Resharper plugin. Our Resharper code style settings file can be found in the mainline trunk, and attached to the end of this page. We also use FxCop to perform conformance analysis.
File Organization
- Files shall be named the same as the class, and shall not exceed 2000 LOC
- Partial class files shall be named in the form of FullClass.PartialPart.cs (e.g. X86Compiler.OpCodes.cs, X86Compiler.Engine.cs, etc.)
- Directories shall be named and nested according to the namespace
- Compiled assemblies shall be named the same as the namespace enclosed in them. e.g. System\IO\*.cs builds into System.IO.dll
Indentation & Alignment
- Indent with four spaces. Do NOT use tabs, as tabs are set to differing levels in different systems. This badly screws up horizontal code alignment.
- Lines shall not exceed 120 characters.
- If breaking a line is necessary, break after a comma or after an operator. The continuation of the statement should be aligned to the beginning of the statement on the previous line
foo = SomeThingBad(this, that, other, blah, baz, meh, eww, gross); - If applicable, horizontally align blocks of code, such as blocks of if-else-if-else statements, array initializers, etc.
if (this) { DoSomething(); } else if (thatAndOther) { DoOtherThing(); } else if (someOtherThing) { DoNothing(); } else { GoAway(); } - Align code of anonymous methods with the delegate declaration,
- Start the delegate declaration on a new line (indented one level)
- Provide a name for the delegate using a comment, to facilitate refactoring into a real method.
MyDelegate myDelegate = delegate(string firstName, string lastName) // CreateFullName { string newName = firstName + " " + lastName; return newName; } - Case statements of a switch shall be indented from the switch statement. Multi-line case block code shall be aligned with the first statement in the code block. Single statements can be combined onto the case statement line.
switch (foo) { case 01: This(); That(); Other(); break; case 02: DoSomethingQuick(); break; case 45: break; } - Indent base class constructors from a constructor's declaration
public Foo() : base("that", false) { .... }- Unless the constructor is a proxy.
public Foo() : base("that", false) { }
- Unless the constructor is a proxy.
Commenting
- Comment to explain algorithm purpose, and comment parameters / variables / properties only to the extent of informing a programmer of its usage. Write external documentation to explain overall architectural details. More commenting is better, but excessive commenting clutters the readability of the code itself.
- All code files shall begin with a code header, describing the copyright, authors, and a short description of the purpose of the classes in the file.
/* * (c) 2008 The Ensemble OS Project * http://www.ensemble-os.org * All Rights Reserved * * This code is covered by the New BSD License, found in license.txt * * Authors: * <OTHER AUTHORS> * <YOU> * * <FILENAME>: <FUNCTIONALITY DESCRIPTION> */
Braces and Parentheses
- Place opening braces on their own line, aligned with the start of the previous statement that scopes the enclosing code block. This is referred to as the BSD style of bracing, and keeps code blocks visually uniform.
- Control structures should always have a brace denoting their code block, even for single-statement blocks.
- Code blocks with short statements can be placed on the same line as the scoping statement
if (foo) { DoThis(); return; } else { Have(); ToDo(); LotsOfOther(); Stuff(); } - Use parentheses to make scope clearer. Avoid excessive parenthesis when scope is already clear.
- Use parentheses to enclose conditional clauses
b = (b < 0 ? -b : b); NOT b = b < 0 ? -b : b; HOWEVER, the following is fine: DoSomething(b < 0 ? -b : b);
Declarations
- Variables with the same data type should be declared on separate lines.
- All class variables shall be declared at the beginning of the class.
- Method local variables should be declared as close to their first usage as possible.
- Initialize variables at the same location as they are declared, if possible.
- Use the "using" variable scope declaration whenever possible.
- Do NOT use arbitrary desctructor methods, e.g
NOT try { OpenSatelliteConnection(); ... } finally { CloseSatelliteConnection(); } RATHER using(SatelliteConnection c = new SatelliteConnection()) { ... } OR using(SatelliteConnection c = OpenSatelliteConnection()) { ... }
Whitespace
- Use a blank line inside methods to separate important blocks of algorithms.
- Use a blank line to separate variables from method code and/or method declarations.
- Use a single space after the comma to separate parameters in a parameter list.
- Do not use a space before the first parameter, and after the last parameter, in a parameter list
DoSomething(a, b, c); NOT DoSomething( a, b, c ); - Do not use a space between the method name and its parameter list's parenthesis
DoSomething(a, b, c); NOT DoSomething (a, b, c); - Use a space between a control structure keyword and its conditional list parenthesis
if (a == b) NOT if(a == b) - Use a space to separate operands from logical operators, except unary operators
a = b; a++; NOT a=b; a ++; - Variable indexers shall follow the same guidelines as method calling
foo[0, 2] NOT foo [0,2] foo[ 0,2 ]
Naming and Capitalization
- The .NET class libraries use US English for the naming scheme (e.g. Initialize, not Initialise). International English and other languages will not be tolerated.
- Use PascalCasing for naming namespaces, classes, interfaces, enums, methods, properties, events, and delegates (i.e. Types).
- Use camelCasing for member variables and method parameters
- Do not prefix variables with underscores, type inference acronyms (Hungarian Notation), or any other variable prefixing
- Prefix interface names with I (e.g. IFooProvider)
- Suffix exception classes with Exception
- Suffix attribute classes with Attribute
- Prefix event handlers with On, with parameters sender and e (e.g.
OnInterrupt(object sender, InterruptEventArgs e)) - Suffix event argument classes with EventArgs
- Suffix abstract classes with Base.
- Suffix classes that inherit from a base class with the original name of the class, minus the Base (if present). E.g:
public abstract class CommandBase { ... } public class EchoCommand : CommandBase { ... }
Visibility Order
- Member variables and methods shall be declared in the order of public, internal, protected, then private
- Member variables shall always be private. Expose variables through public, internal, or protected properties and method
- Enums, delegates, and events shall be declared first
- Static member variables shall be declared afterwards, then class member variables
- Static, then class, member properties to access the variables shall be declared afterwards
- Static methods shall be declared afterwards, then constructors, then public, internal, protected, and private methods
Miscellaneous
- Use the C# null coalescing operator whenever possible
bar = (bar ?? new Object()); NOT bar = (bar == null ? bar : new Object());- Use of other shortcut keywords is highly encouraged.
- Use the C# 3.5 var data type only in situations where a static type cannot otherwise be used (e.g. LINQ expressions)
- Reduce the number of namespace using statements in a code file to only what is necessary. Some of the using statements auto-inserted by Visual
Studio are almost never necessary. - Use #regions to enclose internal, public, and private members in a class. Leave only the public members "unwrapped"
- Further group methods in #regions to denote similarity in usage (e.g. #region Type Dynamic Loaders)
- #regions may be placed inside methods, however, the developer may need to reconsider the atomicity of the method if #regions are required in the method.
- Do not use a hard-to-understand statement, no matter how short they make your code. The following is considered as bad practice:
z = a[a[0,0]++, ++a[0,1]] + (++q) - z++;
- Along the same lines prefix and postfix increment operators MUST NOT be used in the same expression and they SHOULD NOT be used in the same statement.