Version 10 (modified by 16 years ago) ( diff ) | ,
---|
MapGuide Coding Standards
This document was originally based on a document maintained by Bruce Deschant, entitled MapGuide Coding Standards and Guidelines, rev 1.6, last updated July 7, 2008.
Revision History
Revision | Date | Author | Comment |
1.6 | July 7, 2008 | Bruce Deschant | Initial public revision |
Table of Contents
- Introduction
- Consistency with .NET
- Class Names
- Class Variables
- Documentation Standard
- Object Creation and Runtime Speed
- Threading Guidelines
- Source File Naming
- Folder Hierarchy
- Deleting Files and Projects
- General Structure of MapGuide Files
- IDE Settings
- Stylistic Guidelines
Introduction
This document describes the coding guidelines to be used for the MapGuide project. Here are a few reasons why we need coding guidelines:
- Most of the cost of software goes to maintenance
- The maintenance is done by many different individuals over the lifetime of a given software product and not the original developer.
- Guidelines improve readability of the software and help make the learning curve of new developers easier.
Consistency with .NET
Parts of MapGuide will be based on Microsoft's .NET framework, and therefore we should understand and follow the standards/design guidelines recommended by Microsoft for .NET. In Visual Studio .NET, you can find these under Design Guidelines for Class Library Developers. Alternatively, you can go to the help index and navigate to:
Visual Studio .NET .NET Framework Reference Design Guidelines for Class Library Developers
The guidelines discuss topics such as naming conventions, how to decide whether something should be a property or method, proper use of enumerations, delegates, and events, and lots of other things. You should become generally familiar with these guidelines.
Class Names
All class names should be prefixed with “Mg”.
Examples:
class MgServerSelectFeatures class MG_SERVER_MAPPING_API MgMappingUtil class MG_SERVER_RENDERING_API MgServerRenderingService : public MgRenderingService
Class Variables
Any variable that needs to be visible outside of a class will expose itself via accessors (either protected, public, or internal). In general we only use private member variables unless there is a very good reason for any other type. Consequently, variables may be accessed directly only inside the class where they are defined. All other access is done through the accessor. This will help to support multi-threading and distributed objects.
Documentation Standard
.NET/C++
When you define a new public class, property, or method, you must follow the XML Doc standard. Typing three slashes “/” just before a class/property/method declaration will bring up a skeleton XML Doc header in .NET, for C++ you will have to enter this. You then simply need to add information in the different fields. Note that if you fail to add the XML Doc header for .NET, the code may not compile (this is an option that we will enable for all our projects).
XML Doc Standard example:
///<summary> ///Summary of CanIgnoreElement. ///</summary> ///<remarks> /// ///</remarks> ///<param name="element"> ///Description of parameter element ///</param> ///<param name="sFileName"> ///Description of parameter sFileName ///</param> /// <returns></returns> private bool CanIgnoreElement(Element element, string sFileName)
General
It’s important to stay disciplined to avoid getting into the habit of "coding now, documenting later". Whenever you add or modify any classes/properties/methods, you must provide/update the documentation as well. The documentation you write needs to be of sufficient quality so that someone other then you can work with it. We as developers are ultimately responsible for providing the technical information that is required to fully document a class/property/method – not someone else. We’ll refer to this documentation as "seed documentation", and its content should be as close as possible to the end-user reference documentation.
Note that private variables/properties/methods should be documented as well (for code readability). In this case, however, simply use the normal C++ comment style, e.g.
// the shoe size, including half-sizes private float shoeSize
Object Creation and Runtime Speed
As with Java, unnecessary object creation is something you should avoid whenever possible. Just be smart about it when you code. For example, if a call to a method returns a new object and you call that method multiple times in the same context, then change your code to call this method only once and reuse the returned value. Another example: use private helper objects when it makes sense to avoid unnecessary object creation.
Threading Guidelines
It’s important to follow strict threading rules when writing new code. Some of the MapGuide code will be used in a multithreaded deployment environment. Failing to follow multi-threading guidelines will be costly in the future. There are detailed guidelines provided by Microsoft's Design Guidelines for Class Library Developers ([ms-help://MS.VSCC/MS.MSDNVS/cpgenref/html/cpconnetframeworkdesignguidelines.htm local VS link]) that should be reviewed.
Here are some points from those guidelines:
- Avoid providing static methods that alter static state
- Be aware of method calls in locked sections
- Avoid the need for synchronization if possible
- Avoid using global variables
Source File Naming
The naming of source code files will match the class names except that the “Mg” prefix will be dropped. For example, if I have a class called MgClassA then all source files for that class will have the name ClassA. If this class is defined in C#, then there is only one source file with the .cs extension. If this class is defined in C++, then there are two files: the source file with extension .cpp and the header file with extension .h. No mismatches between class names and file names are allowed!
One consequence of this rule is that you can only define one class/enumeration/struct/interface per file.
Folder Hierarchy
Just as the file name reflects the class name, the folder hierarchy will reflect the namespace or module hierarchy. If MgClassA is defined in the namespace OSGeo.MapGuide.DataAccess, then relative to the project root all source files for MgClassA will reside in the OSGeo\MapGuide\DataAccess folder. You will see this hierarchy reflected in the source control application. Again, having the hierarchies match up makes it very easy to navigate to where you want to go.
A consequence of this rule is that if you change a namespace or module then you will also have to rename or restructure the file hierarchy.
Deleting Files and Projects
Eventually everyone needs to remove files and/or projects from the main source. If you’re removing files, you update the Visual Studio solution file and if applicable the Linux build script. Having done this, you still need to remove the file / project from the source control application.
General Structure of MapGuide Files
We would like all MapGuide files to have a similar structure:
- Copyright info
- every source file must have this at the top
- the actual text in the copyright is subject to change, but that doesn’t mean we shouldn’t add it
- whenever you create a new file, find another file with the copyright and paste that into your new file
- Import of .NET namespaces (If applicable)
- ideally sorted alphabetically
- Import of local namespaces (If applicable)
- ideally sorted alphabetically
- Namespace info (If applicable)
- as mentioned above, the namespace matches the folder path of the source file(s)
- Class declaration
- includes seed documentation
- Constant declarations
- Local variable declarations
- Inner classes separated by section comments (If applicable)
- Constructors
- Properties/methods
For example:
// // Copyright (C) 2004-2008 by Autodesk, Inc. // // This library is free software; you can redistribute it and/or // modify it under the terms of version 2.1 of the GNU Lesser // General Public License as published by the Free Software Foundation. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA // using System; using System.Runtime.Serialization; namespace OSGeo.MapGuide.DataAccess.Tools { /// <summary> /// Provides the base functionality for ... /// </summary> [Serializable] public class MyClass : ISerializable { //------------------------------------------------------- // Constants //------------------------------------------------------- Constants //------------------------------------------------------- // Variables //------------------------------------------------------- Variables //------------------------------------------------------- // Inner classes //------------------------------------------------------- Inner classes //------------------------------------------------------- // Constructors //------------------------------------------------------- Constructors //------------------------------------------------------- // Properties / Methods //------------------------------------------------------- Properties / Methods } }
IDE Settings
It is recommended that you use Visual Studio .NET for software development on Windwos and it helps if some settings are the same for all users. Here are the ones you should make sure are set.
Under Tools/Options/Text Editor/All Languages/Tabs, make sure the tab size is set to 4 and that the ‘Insert spaces’ option is turned on. The use of spaces everywhere rather than a mix of tabs and spaces has the following advantages:
- It reduces the number of non-essential differences that show up when comparing files
- If I have my editor settings set to use spaces and I edit an area that contains tabs, Visual Studio will sometimes replace the tabs with spaces
- It prevents columns from lining up evenly
- File comparison tools may treat the width of tabs & spaces unevenly (1 tab does not exactly equal 4 spaces)
- Viewing a source file in Notepad is even worse: a tab is the equivalent of about 8 spaces
Stylistic Guidelines
General
Use the next-line convention for curly braces:
public void Foo() { }
Another coding standard for C# is to preface all references to instance variables/properties/methods with the “this” keyword. For example:
public void Foo() { // update an instance member variable this.index = 0; // call an instance member function this.Foo(); }
From a stylistic viewpoint, this makes it very easy to distinguish which variables are member variables and which have temporary scope. In C++ this is typically done by prefixing names of member variables using “m_” and therefore the keyword “this” is not required.
All static variables are named using Pascal casing according to the .NET standard. When referencing static constants or methods, always prefix it using the actual name of the class defining the constant or static method. For example:
public class MgFoo1 { /// <summary> /// The max iterations to use. /// </summary> public int maxIter; /// <summary> /// Default value for max iterations. /// </summary> static public int defaultMaxIterations = 10; ... } public class MgFoo2 extends MgFoo1 { ... this.maxIter = this.defaultMaxIterations; // wrong – not an instance variable this.maxIter = MgFoo2.defaultMaxIterations; // wrong - subclass doesn't declare it this.maxIter = MgFoo1.defaultMaxIterations; // correct ... }
Compiler Warning
This guideline strongly encourages all compiler warnings to be treated as errors and therefore they should be fixed.
Strings
Internally
For the MapGuide project the internal string format to use will be wstring. For .NET development the String class will be used.
Across Wire
The UTF-8 standard will be used for transmitting strings across the wire between any of the MapGuide products (client, web tier, and server).
Repository
The resource repository used by MapGuide will use the UTF-8 standard for storing strings.
API Boundary
MapGuide will use the following string format for API boundaries in the web tier and the server:
!#cpp const wstring&
Break and Goto
Goto and break make the code very hard to read. I cannot believe that C# supports goto!!! The guideline is to avoid using break statements inside loops and to forbid the use of goto.
If, Else-If, Else Statements
The if-else statements should have the following form:
!#cpp if (condition) { statements; } if (condition) { statements; } else { statements; } if (condition) { statements; } else if (condition) { statements; } else { statements; }
For Statements
A for statement should have the following form:
for (initialization; condition; update) { statements; }
An empty for statement
for (initialization; condition; update);
Variable declaration inside for statement should NOT be done.
Don’t do this:
for (int i=0; i<10; i++) { statements; }
Do this instead:
int i=0; for (i=0; i<10; i++) { statements; }
ForEach Statements
A foreach statement should have the following form:
foreach (var in col) { statements; }
While Statements
A while statement should have the following form:
while (condition) { statements; }
An empty while statement should have the following form:
while (condition);
Do-While Statements
A do-while statement should have the following form:
do { statements; } while (condition);
Switch Statements
A switch statement should have the following form. C# requires a terminating break or goto for each case.
switch (condition) { case XYZ: statements; break; case ZYX: statements; break; default: statements; break; }
Every switch statement should include a default case.
Try-Catch Statements
A try-catch statement should have the following format:
try { statements; } catch (MgExceptionClass1 e) { statements; } catch (MgExceptionClass2 e) { statements; }
A try-catch statement may also be followed by finally
which is always executed.
try { statements; } catch (MgExceptionClass1 e) { statements; } catch (MgExceptionClass2 e) { statements; } finally { statements; }
Return Value
The use of a single return within a method or function is encouraged. The following structure:
if (booleanExpression) { return true; } else { return false; }
should be:
return booleanExpression;
Also,
if (condition) { return x; } return y;
should be:
return (condition ? x : y);
Expressions With !==
The following order should be used because it will help catch the accidental use of "!=" when "!==" was intended at compile time:
if (constant == variable) { // do something } else { // do something else }