Coding Standard and Guidelines: Difference between revisions
(29 intermediate revisions by 5 users not shown) | |||
Line 10: | Line 10: | ||
# 'Uncrustify' and 'StyleCop' and how we use them with Petra are described in [[Coding Standards Tools: 'Uncrustify' and 'StyleCop']]. | # 'Uncrustify' and 'StyleCop' and how we use them with Petra are described in [[Coding Standards Tools: 'Uncrustify' and 'StyleCop']]. | ||
This 'C# Coding Style Guide' was inspired by the [http://www.icsharpcode.net/TechNotes/ C# Coding Style Guide] | This 'C# Coding Style Guide' was inspired by the [http://www.icsharpcode.net/TechNotes/ C# Coding Style Guide] written by Mike Krüger. This is used by the SharpDevelop programmers who work on the SharpDevelop IDE. | ||
=File Organization= | =File Organization= | ||
Line 16: | Line 16: | ||
==C# Sourcefiles== | ==C# Sourcefiles== | ||
* Keep your classes/files short: try hard not to exceed 3.000 lines of code | * Keep your classes/files short: try hard not to exceed 3.000 lines of code and never go beyond 4.000 lines of code. | ||
** Exception to that | ** Exception to that: | ||
*** Auto-generated code can ignore that rule if the generated file doesn't need to be looked at/touched by developers. | *** Auto-generated code can ignore that rule if the generated file doesn't need to be looked at/touched by developers. | ||
* Divide your code up | * Divide your code up and make structures clearer. | ||
* Every class should go in a separate file. | * Every class should go in a separate file. | ||
** Exceptions to that | ** Exceptions to that: | ||
*** Helper classes for the main class in the same file are allowed | *** Helper classes for the main class in the same file are allowed, but they must be private classes! | ||
*** Auto-generated code can ignore that rule if the generated file doesn't need to be looked at/touched by developers. | *** Auto-generated code can ignore that rule if the generated file doesn't need to be looked at/touched by developers. | ||
* The file name should reflect the name of the class in the file (.cs as extension of course). | * The file name should reflect the name of the class in the file (.cs as extension of course). | ||
** Exception to that | ** Exception to that: | ||
*** The file name of a source file can have the last part of a Namespace in it ''if'' the Namespace is nested quite deeply ''and'' | *** The file name of a source file can have the last part of a Namespace in it ''if'' the Namespace is nested quite deeply ''and'' | ||
the directory structure doesn't reflect that deep nesting anymore (see [[Coding_Standard_and_Guidelines#Directory_Layout|'Directory Layout']] below). Example: DataAggregates.PPartnerAddress.cs in Namespace 'Ict.Petra.Server.MPartner.'''DataAggregates'''' in directory 'U:\openpetraorg\csharp\ICT\Petra\Server\lib\MPartner'. | the directory structure doesn't reflect that deep nesting anymore (see [[Coding_Standard_and_Guidelines#Directory_Layout|'Directory Layout']] below). Example: DataAggregates.PPartnerAddress.cs in Namespace 'Ict.Petra.Server.MPartner.'''DataAggregates'''' in directory 'U:\openpetraorg\csharp\ICT\Petra\Server\lib\MPartner'. | ||
Line 35: | Line 35: | ||
* Create a directory for every namespace. Example: for Namespace 'Ict.Petra.Server.App.Core' use 'U:\delphi.net\ICT\Petra\Server\app\' as the path (do not use the Namespace name with dots for directory names). | * Create a directory for every namespace. Example: for Namespace 'Ict.Petra.Server.App.Core' use 'U:\delphi.net\ICT\Petra\Server\app\' as the path (do not use the Namespace name with dots for directory names). | ||
** Exceptions to that | ** Exceptions to that: | ||
*** Additional directories might be placed inbetween parts of the Namespace ''if'' that benefits the directory structure ''and'' | *** Additional directories might be placed inbetween parts of the Namespace ''if'' that benefits the directory structure ''and'' an agreed rule when to do that is followed. Example: Namespace 'Ict.Petra.Server.MPartner.DataAggregates' resides in directory 'U:\delphi.net\ICT\Petra\Server\'''lib'''\MPartner'. Note the 'lib' directory, which is inserted. | ||
*** ''If'' a Namespace is nested quite deeply, we can opt to have the directory structure ''not'' reflecting that deep nesting. In that case, the last part of a Namespace should then be part of the file name of a Class (see [[Coding_Standard_and_Guidelines#C.23_Sourcefiles |'C# Sourcefiles']] above). | *** ''If'' a Namespace is nested quite deeply, we can opt to have the directory structure ''not'' reflecting that deep nesting. In that case, the last part of a Namespace should then be part of the file name of a Class (see [[Coding_Standard_and_Guidelines#C.23_Sourcefiles |'C# Sourcefiles']] above). | ||
See also [['Explanation of Directory Structure and Rules']] for more detailed information! | |||
Line 50: | Line 53: | ||
Specifically, '''we indent everything with four spaces''' - <font color="blue">Enforced by Uncrustify</font>. This applies for the indendation from column 1 to the indendation level of the current code and also to any further indendations from there. | Specifically, '''we indent everything with four spaces''' - <font color="blue">Enforced by Uncrustify</font>. This applies for the indendation from column 1 to the indendation level of the current code and also to any further indendations from there. | ||
By using spaces, not tabs, we can ensure that our code is indented identically for every developer and in every editor. | By using spaces, not tabs, we can ensure that our code is indented identically for every developer and in every editor. If we used tabs, the indentation could vary. | ||
'''Don't use tabs for indentation - use spaces!''' <font color="blue">Enforced by Uncrustify.</font> | '''Don't use tabs for indentation - use spaces!''' <font color="blue">Enforced by Uncrustify.</font> | ||
Line 56: | Line 59: | ||
==Wrapping Lines== | ==Wrapping Lines== | ||
When an expression will not fit on a single line, break it up before it reaches more than 150 characters. <font color="blue">Enforced by Uncrustify.</font> You might want to break it up before that for readability. | When an expression will not fit on a single line, break it up before it reaches more than '''150''' characters. <font color="blue">Enforced by Uncrustify.</font> You might want to break it up before that for readability. | ||
Follow these general principles when breaking up a line: | Follow these general principles when breaking up a line: | ||
Line 66: | Line 69: | ||
Example of breaking up method calls: | Example of breaking up method calls: | ||
LongMethodCall(expr1, expr2, | |||
LongMethodCall(expr1, expr2, | |||
expr3, expr4, expr5); // indented at the standard indentation level | |||
or | or | ||
LongMethodCall(expr1, expr2, | |||
LongMethodCall(expr1, expr2, | |||
expr3, expr4, expr5); // aligned with the beginning of the expression on the previous line | |||
The first is preferred because it results in uniformly formatted code, but if indenting with the second method makes for better reading in a specific case then use the second method. | The first is preferred because it results in uniformly formatted code, but if indenting with the second method makes for better reading in a specific case then use the second method. | ||
Line 87: | Line 86: | ||
PREFER: | PREFER: | ||
var = a * b / (c - g + f) + | |||
var = a * b / (c - g + f) + | |||
4 * z; | |||
BAD STYLE – AVOID: | BAD STYLE – AVOID: | ||
var = a * b / (c - g + | |||
var = a * b / (c - g + | |||
f) + 4 * z; | |||
The first is preferred, since the break occurs outside the paranthesized expression (higher level rule). | The first is preferred, since the break occurs outside the paranthesized expression (higher level rule). | ||
Line 127: | Line 122: | ||
Block comments may be useful in rare cases, refer to the TechNote [http://www.icsharpcode.net/TechNotes/Commenting20020413.pdf 'The fine Art of Commenting'] for an example. | Block comments may be useful in rare cases, refer to the TechNote [http://www.icsharpcode.net/TechNotes/Commenting20020413.pdf 'The fine Art of Commenting'] for an example. | ||
'' | ''As a general rule of thumb, the length of a comment should not overly exceed the length of the code it explains as this is an indication of too complicated (and potentially buggy) code.'' | ||
''For | ''For commenting out sections of code, use the 'Single Line Comments' (despite their name) to distinguish better between code that has comments attached and code that is commented out.'' | ||
==Single Line Comments== | ==Single Line Comments== | ||
Developers should use the <code>//</code> (two forward slahes) comment style to "comment out" code (SharpDevelop has a shotcut key for it, Alt+/). ''It may be used for commenting out sections of code too.'' | |||
Note on using StyleCop: because we will use StyleCop, rather use <code>////</code> (four forward slashes) instead of <code>//</code> when commenting out ''single'' lines of code. This helps the tool to not confuse it with a single line comment (which is not commented out code). (Refers to StyleCop Rule SA1512: SingleLineCommentsMustNotBeFollowedByBlankLine.) | Note on using StyleCop: because we will use StyleCop, you should rather use <code>////</code> (four forward slashes) instead of <code>//</code> when commenting out ''single'' lines of code. This helps the tool to not confuse it with a single line comment (which is not commented out code). (Refers to StyleCop Rule SA1512: SingleLineCommentsMustNotBeFollowedByBlankLine.) | ||
Line 172: | Line 167: | ||
The first category contains tags like <code><summary></code>, <code><param></code> or <code><exception></code>. | The first category contains tags like <code><summary></code>, <code><param></code> or <code><exception></code>. | ||
These | These tags represent the elements of a program's API which must be documented for the program to be useful to other programmers. | ||
They usually have attributes such as <code>name</code> or <code>cref</code> (as demonstrated in the multi line example above) and are | |||
checked by the compiler, so they should be valid. | |||
The latter category governs the layout of the documentation, using tags such as <code><nowiki><code></nowiki></code>, <code><list></code> or <code><para></code>. | The latter category governs the layout of the documentation, using tags such as <code><nowiki><code></nowiki></code>, <code><list></code> or <code><para></code>. | ||
For a fuller explanation of XML comments see [[ | For a fuller explanation of XML comments see [['CSharp In-Code Documentation with MS XML Tags']]. | ||
For information on how to produce API-style documentation for C# Projects whose .cs files contain XML Comment Tags see [[ | For information on how to produce API-style documentation for C# Projects whose .cs files contain XML Comment Tags, see [['How to produce CSharp API Documentation']]. | ||
For information on commenting best practice and further issues related to commenting, see the TechNote [http://www.icsharpcode.net/TechNotes/Commenting20020413.pdf 'The fine Art of Commenting']. | For information on commenting best practice and further issues related to commenting, see the TechNote [http://www.icsharpcode.net/TechNotes/Commenting20020413.pdf 'The fine Art of Commenting']. | ||
Line 214: | Line 209: | ||
The above example also demonstrates the drawbacks of non-obvious variable names. | The above example also demonstrates the drawbacks of non-obvious variable names. | ||
''Be clear when naming variables.'' Using self-explanatory variable names such as <code>IndentLevel</code> | ''Be clear when naming variables.'' Using self-explanatory variable names such as <code>IndentLevel</code> eradicates the need for making explanatory comments. | ||
==Initialization== | ==Initialization== | ||
Line 229: | Line 224: | ||
Initialisations should be followed by an empty line so that they are visually set apart from the rest of the source code. <font color="blue">Enforced by Uncrustify if the initialisations are part of declarations.</font> | Initialisations should be followed by an empty line so that they are visually set apart from the rest of the source code. <font color="blue">Enforced by Uncrustify if the initialisations are part of declarations.</font> | ||
Note: | '''Note:''' when initializing a Dialog/Modal Form (by using .ShowDialog instead of .Show), use the following construct to be sure the Form is released from memory when it is closed: | ||
using (TOpenFileDialog openFileDialog = new TOpenFileDialog()) | |||
using (TOpenFileDialog openFileDialog = new TOpenFileDialog()) | { | ||
{ | openFileDialog.ShowDialog(); | ||
... | |||
} <font color="gray">// .NET will release openFileDialog automatically, so we can't forget about it!</font> | |||
} <font color="gray">// .NET will release openFileDialog automatically, so we can't forget about it!</font | |||
Background: | '''Background:''' forms shown with .ShowDialog are not released from memory, but kept in memory so the caller can call methods after the Form is closed to retrieve parameters. Therefore, you can forget to release the form from memory later. | ||
==Class, Interface and Namespace Declarations== | ==Class, Interface and Namespace Declarations== | ||
Line 254: | Line 247: | ||
* Code block separation | * Code block separation | ||
** An empty line should preceed and succeed each class, interface or namespace block. | ** An empty line should preceed and succeed each class, interface or namespace block. | ||
*** Exceptions | *** Exceptions: | ||
**** if a class, interface or namespace block begins immediately after another class, interface or namespace block has been started with an opening brace "{": no empty line. | **** if a class, interface or namespace block begins immediately after another class, interface or namespace block has been started with an opening brace "{": then no empty line should be inserted. | ||
**** if a class, interface or namespace block ends and another enclosing code block also ends immediately after the closing brace "}": no empty line. | **** if a class, interface or namespace block ends and another enclosing code block also ends immediately after the closing brace "}": then no empty line should be inserted. | ||
For example: | For example: | ||
namespace MyNamespace | |||
namespace MyNamespace | '''{''' <font color="gray">// no empty line</font> | ||
'''{''' <font color="gray">// no empty line</font> | class TMySample : TMyClass, IMyInterface | ||
'''{''' | |||
void DoSomething() | |||
'''{''' | |||
'''}''' | |||
void DoAnotherThing() | |||
'''{''' | |||
'''}''' | |||
'''}''' | |||
<font color="gray">// empty line goes here</font> | |||
class TMyOtherSample : TMySample, IMyOtherInterface | |||
'''{''' | |||
void DoSomething() | |||
'''{''' | |||
'''}''' | |||
void DoAnotherThing() | |||
'''{''' | |||
'''}''' | |||
'''}''' <font color="gray">// no empty line</font> | |||
'''}''' | |||
'''}''' | |||
For another brace placement example look at section 'Code Examples - Brace placement example'. | For another brace placement example look at section 'Code Examples - Brace placement example'. | ||
Line 308: | Line 299: | ||
For example: | For example: | ||
<font color="gray">// empty line goes here</font> | |||
public MySample'''('''int AMyInt''')''' | |||
public MySample'''('''int AMyInt''')''' | '''{''' | ||
'''{''' | this.FMyInt = AMyInt; | ||
'''}''' | |||
'''}''' | <font color="gray">// empty line goes here</font> | ||
void Inc'''()''' | |||
void Inc'''()''' | '''{''' | ||
'''{''' | ++FMyInt; | ||
'''}''' | |||
'''}''' | <font color="gray">// empty line goes here</font> | ||
<font color="blue">Enforced by Uncrustify: all rules</font> | <font color="blue">Enforced by Uncrustify: all rules</font> | ||
Line 329: | Line 318: | ||
* Braces | * Braces | ||
** Class braces placement rules should be applied. <font color="blue">Enforced by Uncrustify: all rules, except for 'opening curly bracket must be on new line' (Uncrustify can't enforce that for EventHandlers).</font> | ** Class braces placement rules should be applied. <font color="blue">Enforced by Uncrustify: all rules, except for 'opening curly bracket must be on a new line' (Uncrustify can't enforce that for EventHandlers).</font> | ||
* Spacing | * Spacing | ||
** For properties where both 'get' and 'set' blocks are present, an empty line should be placed between the 'get' block and the 'set' block. | ** For properties where both 'get' and 'set' blocks are present, an empty line should be placed between the 'get' block and the 'set' block. | ||
Line 340: | Line 329: | ||
For example: | For example: | ||
<font color="gray">// empty line goes here</font> | |||
public int Amount | |||
public int Amount | '''{''' | ||
'''{''' | get | ||
'''{''' | |||
... | |||
'''}''' | |||
<font color="gray">// empty line goes here</font> | |||
set | |||
'''{''' | |||
... | |||
'''}''' | |||
'''}''' | |||
'''}''' | <font color="gray">// empty line goes here</font> | ||
public EventHandler MyCustomEventHandler | |||
public EventHandler MyCustomEventHandler | '''{''' | ||
'''{''' | add | ||
'''{''' | |||
... | |||
'''}''' | |||
<font color="gray">// empty line goes here</font> | |||
remove | |||
'''{''' | |||
... | |||
'''}''' | |||
'''}''' | |||
'''}''' | <font color="gray">// empty line goes here</font> | ||
public this'''['''string index] | |||
public this'''['''string index] | '''{''' | ||
'''{''' | get; | ||
<font color="gray">// empty line goes here</font> | |||
set; | |||
'''}''' | |||
'''}''' | <font color="gray">// empty line goes here</font> | ||
==Simple Statements== | ==Simple Statements== | ||
Line 398: | Line 385: | ||
In a situation where more than one condition is specified (eg. in <code>if</code>, <code>for</code> or <code>do-while</code>-statments), please follow these guidelines: | In a situation where more than one condition is specified (eg. in <code>if</code>, <code>for</code> or <code>do-while</code>-statments), please follow these guidelines: | ||
* Place each condition on a separate line. | * Place each condition on a separate line. | ||
* Place the ''&&'' and ''||'' that connects the conditions on the same line as the next condition. ( | * Place the ''&&'' and ''||'' that connects the conditions on the same line as the next condition. (See example.) | ||
* If you have a very complicated condition statement, try to align the different conditions. The goal is to make the condition more readable in the end, so you may use | * If you have a very complicated condition statement, try to align the different conditions. The goal is to make the condition more readable in the end, so you may use your discretion here as well. | ||
Example: | Example: | ||
if (((''condition1)'' | |||
if (((''condition1)'' | && (''condition2'') | ||
|| (''condition3'' | |||
&& ''condition4'')) | |||
(&& !''condition5'')) | |||
{ | |||
{ | ... | ||
} | |||
} | |||
==If, if-else, if else-if else Statements== | ==If, if-else, if else-if else Statements== | ||
Line 421: | Line 406: | ||
* Spacing | * Spacing | ||
** A space character should be put between "if" and the opening parenthesis "(". | ** A space character should be put between "if" and the opening parenthesis "(". | ||
** No space character should be put between the opening bracket "(" and the | ** No space character should be put between the opening bracket "(" and the ensuing code statement(s), or between the code statement(s) and the closing bracket ")". | ||
* Code block separation | * Code block separation | ||
** An empty line should preceed and succeed the statement block (the same applies to other statements that appear as blocks of code). | ** An empty line should preceed and succeed the statement block (the same applies to other statements that appear as blocks of code). | ||
Line 428: | Line 413: | ||
For example: | For example: | ||
<font color="gray">// empty line goes here</font> | |||
if '''('''condition''')''' | |||
if '''('''condition''')''' | { | ||
{ | DoSomething(); | ||
... | |||
} | |||
} | <font color="gray">// empty line goes here</font> | ||
if '''('''condition''')''' | |||
if '''('''condition''')''' | { | ||
{ | DoSomething(); | ||
... | |||
} | |||
} | else | ||
else | { | ||
{ | DoSomethingOther(); | ||
... | |||
} | |||
} | <font color="gray">// empty line goes here</font> | ||
if '''('''condition''')''' | |||
if '''('''condition''')''' | { | ||
{ | DoSomething(); | ||
... | |||
} | |||
} | else if '''('''condition''')''' | ||
else if '''('''condition''')''' | { | ||
{ | DoSomethingOther(); | ||
... | |||
} | |||
} | else | ||
else | { | ||
{ | DoSomethingOtherAgain(); | ||
... | |||
} | |||
} | <font color="gray">// empty line goes here</font> | ||
<font color="blue">Enforced by Uncrustify: all rules</font> | <font color="blue">Enforced by Uncrustify: all rules</font> | ||
== | ==For / Foreach Statements== | ||
<code>For</code> and <code>foreach</code> statements should follow the following formatting rules: | <code>For</code> and <code>foreach</code> statements should follow the following formatting rules: | ||
Line 475: | Line 458: | ||
* Spacing | * Spacing | ||
** A space character should be put between "for"/"foreach" and the opening parenthesis "(". | ** A space character should be put between "for"/"foreach" and the opening parenthesis "(". | ||
** No space character should be put between the opening bracket "(" and the | ** No space character should be put between the opening bracket "(" and the ensuing code statement(s), or between the code statement(s) and the closing bracket ")". | ||
** <code>for</code> only | ** <code>for</code> only | ||
*** Space characters should be put after the semicolon ";" which separates the initialisation, condition and execution code parts (contained inside the brackets). | *** Space characters should be put after the semicolon ";" which separates the initialisation, condition and execution code parts (contained inside the brackets). | ||
Line 486: | Line 469: | ||
For example: | For example: | ||
<font color="gray">// empty line goes here</font> | |||
for '''('''int Counter = 0; Counter < 5; ++Counter''')''' | |||
for '''('''int Counter = 0; Counter < 5; ++Counter''')''' | { | ||
{ | ... | ||
} | |||
} | <font color="gray">// empty line goes here</font> | ||
foreach '''('''int MyIterator in IntList''')''' | |||
foreach '''('''int MyIterator in IntList''')''' | { | ||
{ | ... | ||
} | |||
} | <font color="gray">// empty line goes here</font> | ||
<font color="blue">Enforced by Uncrustify: all rules</font> | <font color="blue">Enforced by Uncrustify: all rules</font> | ||
== | ==While/Do-While Statements== | ||
<code>while/do</code> statements should follow the following formatting rules: | <code>while/do</code> statements should follow the following formatting rules: | ||
Line 510: | Line 491: | ||
* Spacing | * Spacing | ||
** A space character should be put between "while" and the opening parenthesis "(". | ** A space character should be put between "while" and the opening parenthesis "(". | ||
** No space character should be put between the opening bracket "(" and the condition, | ** No space character should be put between the opening bracket "(" and the condition, or between the condition and the closing bracket ")". | ||
* Code block separation | * Code block separation | ||
** An empty line should preceed and succeed the statement block (the same applies to other statements that appear as blocks of code). | ** An empty line should preceed and succeed the statement block (the same applies to other statements that appear as blocks of code). | ||
Line 517: | Line 498: | ||
For example: | For example: | ||
<font color="gray">// empty line goes here</font> | |||
while '''('''condition''')''' | |||
while '''('''condition''')''' | { | ||
{ | ... | ||
} | |||
} | <font color="gray">// empty line goes here</font> | ||
An empty while should have the following form: | An empty while should have the following form: | ||
while '''(condition)''' | |||
while '''(condition)''' | { | ||
{ | ; | ||
} | |||
} | |||
A do-while statement should have the following form: | A do-while statement should have the following form: | ||
<font color="gray">// empty line goes here</font> | |||
do | |||
do | { | ||
{ | ... | ||
} while '''('''condition''')'''; | |||
} while '''('''condition''')'''; | <font color="gray">// empty line goes here</font> | ||
<font color="blue">Enforced by Uncrustify: all rules</font> | <font color="blue">Enforced by Uncrustify: all rules</font> | ||
== | ==Switch Statements== | ||
A <code>switch</code> statement should follow the following formatting rules: | A <code>switch</code> statement should follow the following formatting rules: | ||
Line 556: | Line 531: | ||
* Spacing | * Spacing | ||
** A space character should be put between "switch" and the opening parenthesis "(". | ** A space character should be put between "switch" and the opening parenthesis "(". | ||
** No space character should be put between the opening bracket "(" and the condition, | ** No space character should be put between the opening bracket "(" and the condition, or between the condition and the closing bracket ")". | ||
* Code block separation | * Code block separation | ||
** An empty line should preceed and succeed the statement block (the same applies to other statements that appear as blocks of code). | ** An empty line should preceed and succeed the statement block (the same applies to other statements that appear as blocks of code). | ||
** An empty line should succeed each 'break;' statement. | ** An empty line should succeed each 'break;' statement. | ||
*** Exception: no empty line after the 'break;' statement in the 'default' block. | *** Exception: no empty line after the 'break;' statement in the 'default' block. | ||
* Other line breaks | * Other line breaks: | ||
** the <code>case ''x'':</code> condition and | ** the <code>case ''x'':</code> condition and its ensuing statement(s), as well as the <code>default:</code> line and its statement(s), should be separated by a line break. | ||
For example: | For example: | ||
<font color="gray">// empty line goes here</font> | |||
switch '''('''condition''')''' | |||
switch '''('''condition''')''' | { | ||
{ | case A: <font color="gray">// line break here</font> | ||
... | |||
break; | |||
<font color="gray">// empty line goes here</font> | |||
case B: <font color="gray">// line break here</font> | |||
... | |||
break; | |||
<font color="gray">// empty line goes here</font> | |||
default: <font color="gray">// line break here</font> | |||
... | |||
break; <font color="gray">// NO empty line to follow!</font> | |||
} | |||
} | <font color="gray">// empty line goes here</font> | ||
<font color="blue">Enforced by Uncrustify: all rules</font> | <font color="blue">Enforced by Uncrustify: all rules</font> | ||
== | ==Try-Catch Statements== | ||
<code>try-catch</code> statements should follow | <code>try-catch</code> statements should follow these formatting rules: | ||
* Braces | * Braces | ||
Line 598: | Line 571: | ||
For example: | For example: | ||
<font color="gray">// empty line goes here</font> | |||
try | |||
try | { | ||
{ | ... | ||
} | |||
} | catch (Exception) | ||
catch (Exception) | { | ||
{ | ... | ||
} | |||
} | <font color="gray">// empty line goes here</font> | ||
or | or | ||
<font color="gray">// empty line goes here</font> | |||
try | |||
try | { | ||
{ | ... | ||
} | |||
} | catch (ApplicationException ae) | ||
catch (ApplicationException ae) | { | ||
{ | ... | ||
} | |||
} | catch (Exception e) | ||
catch (Exception e) | { | ||
{ | ... | ||
} | |||
} | finally | ||
finally | { | ||
{ | ... | ||
} | |||
} | <font color="gray">// empty line goes here</font> | ||
<font color="blue">Enforced by Uncrustify | <font color="blue">Enforced by Uncrustify -</font> all rules, except for: | ||
* 'Code block separation' (Uncrustify can't enforce that); | * 'Code block separation' (Uncrustify can't enforce that); | ||
* 'Opening curly bracket must be on new line' for the 'finally' statement (Uncrustify's rule to enforce that doesn't work). | * 'Opening curly bracket must be on new line' for the 'finally' statement (Uncrustify's rule to enforce that doesn't work). | ||
=White Space= | =White Space= | ||
Line 655: | Line 624: | ||
* Logical sections inside a method to improve readability. | * Logical sections inside a method to improve readability. | ||
Blank lines should be indented to the correct indent level instead of leaving them blank or | Blank lines should be indented to the correct indent level instead of leaving them blank or, even worse, using another indentation level. This makes the insertion of new statements in these lines much easier. | ||
==Inter-term spacing== | ==Inter-term spacing== | ||
Line 662: | Line 631: | ||
For example: | For example: | ||
TestMethod(a, b, c); <font color="gray">// don't use : TestMethod(a,b,c)</font> | |||
TestMethod(a, b, c); <font color="gray">// don't use : TestMethod(a,b,c)</font | |||
Single spaces surround operators (except unary operators like increment (<code>++</code>) or logical not (<code>!</code>)). | Single spaces surround operators (except unary operators like increment (<code>++</code>) or logical not (<code>!</code>)). | ||
For example: | For example: | ||
a = b; <font color="gray">// don't use a=b;</font> | |||
a = b; <font color="gray">// don't use a=b;</font> | |||
for (int Counter = 0; Counter < 10; ++Counter) <font color="gray">// don't use 'for (int i=0; i<10; ++i)' | |||
// or 'for(int i=0;i<10;++i)'</font> | |||
<font color="blue">Enforced by Uncrustify:</font> all rules. | |||
<font color="blue">Enforced by Uncrustify: | |||
Line 695: | Line 660: | ||
===All-Uppercase=== | ===All-Uppercase=== | ||
All-uppercase capitalises all characters of a word. Use all-uppercase only for ''constant identifiers''. (For non-constant identifiers that are commonly used in all-uppercase (eg. URL, PI) use | All-uppercase capitalises all characters of a word. Use all-uppercase only for ''constant identifiers''. (For non-constant identifiers that are commonly used in all-uppercase (eg. URL, PI) use Pascal Casing instead.) | ||
==Naming Guidelines== | ==Naming Guidelines== | ||
Line 705: | Line 669: | ||
in early Windows programming, but is now obsolete or at least should be considered deprecated. Using Hungarian Notation is not allowed if you follow this guide. | in early Windows programming, but is now obsolete or at least should be considered deprecated. Using Hungarian Notation is not allowed if you follow this guide. | ||
'' | ''Remember: a good variable name describes the semantic not the type.'' | ||
An exception to this rule is GUI code. All fields and variable names that contain GUI elements like ''Button'' should be prefixed with their respective [[GUI_controls_prefixes|GUI control prefix]]. | An exception to this rule is GUI code. All fields and variable names that contain GUI elements like ''Button'' should be prefixed with their respective [[GUI_controls_prefixes|GUI control prefix]]. | ||
For example: | For example: | ||
System.Windows.Forms.Button '''btn'''Cancel; | |||
System.Windows.Forms.Button '''btn'''Cancel; | System.Windows.Forms.TextBox '''txt'''Name; | ||
System.Windows.Forms.TextBox '''txt'''Name; | |||
===Namespace Naming Guidelines=== | ===Namespace Naming Guidelines=== | ||
* Namespace names should be nouns or noun phrases. | * Namespace names should be nouns or noun phrases. | ||
* Use | * Use Pascal Casing. | ||
* Name prefix: none. | * Name prefix: none. | ||
Line 723: | Line 685: | ||
* Class names should be nouns or noun phrases. | * Class names should be nouns or noun phrases. | ||
* Use | * Use Pascal Casing. | ||
* Name prefix: 'T' (stands for '''T'''ype | * Name prefix: '<code>T</code>' (stands for '''T'''ype). | ||
** Classes that are derived from <code>System.Windows.Form</code> should use the prefix <code>TFrm</code> instead. | ** Classes that are derived from <code>System.Windows.Form</code> should use the prefix <code>TFrm</code> instead. | ||
For example: | For example: | ||
public class TConnection | |||
public class | { | ||
{ | ... | ||
} | |||
} | |||
public class TFrmAboutPetra | |||
public class | { | ||
{ | ... | ||
} | |||
} | |||
===Interface Naming Guidelines=== | ===Interface Naming Guidelines=== | ||
* Name interfaces with nouns or noun phrases or adjectives describing | * Name interfaces with nouns or noun phrases or adjectives describing behaviour. | ||
* Use | * Use Pascal Casing. | ||
* Name prefix: 'I' (stands for '''I'''nterface | * Name prefix: '<code>I</code>' (stands for '''I'''nterface). | ||
For example: | For example: | ||
public interface IComponent | |||
public interface | { | ||
{ | ... | ||
} | |||
} | |||
===Exception Naming Guidelines=== | ===Exception Naming Guidelines=== | ||
* Name Exceptions with nouns or noun phrases. | * Name Exceptions with nouns or noun phrases. | ||
* Use | * Use Pascal Casing. | ||
* Name prefix: 'E' (stands for '''E'''xception). | * Name prefix: '<code>E</code>' (stands for '''E'''xception). | ||
* Name suffix: '<code>Exception</code>'. (The suffix is a recommended convention of Microsoft and considered good style by many C# programmers.) | |||
For example: | For example: | ||
public class EDBConnectionNotEstablishedException : EOPDBException | |||
public class | { | ||
{ | ... | ||
} | |||
} | |||
===Enum Naming Guidelines=== | ===Enum Naming Guidelines=== | ||
* Name | * Name | ||
** Use singular names for enum ''types''. | ** Use singular names for enum ''types''. Pascal Casing. | ||
** Use singular names for enum ''values''. camelCasing. | ** Use singular names for enum ''values''. camelCasing. | ||
* Prefixes | * Prefixes | ||
Line 780: | Line 737: | ||
For example: | For example: | ||
public enum '''T'''ModuleSwitchEnum | |||
public enum '''T'''ModuleSwitchEnum | { | ||
{ | '''ms'''None, '''ms'''Partner, '''ms'''Personnel, '''ms'''Finance | ||
} | |||
} | |||
public enum '''T'''UIConnectorTypeEnum | |||
public enum '''T'''UIConnectorTypeEnum | { | ||
{ | '''uict'''PartnerKey, '''uict'''LocationKey, '''uict'''NewPartner | ||
} | |||
} | |||
===Event Names=== | ===Event Names=== | ||
Line 797: | Line 752: | ||
Event Handlers: | Event Handlers: | ||
* Name event handlers with the <code>EventHandler</code> suffix. | * Name event handlers with the <code>EventHandler</code> suffix. | ||
* Name event handlers that have a concept of pre and post using the present and past tense (eg. <code>...Chang'''ing'''EventHandler</code> and <code>...Chang'''ed'''EventHandler</code>). | * Name event handlers that have a timeline concept of pre and post using the present and past tense (eg. <code>...Chang'''ing'''EventHandler</code> and <code>...Chang'''ed'''EventHandler</code>). | ||
* For generic event handlers use two parameters named <code>sender</code> and <code>e</code>. | * For generic event handlers use two parameters named <code>sender</code> and <code>e</code>. | ||
* Use | * Use Pascal Casing. | ||
* Prefix: 'T' | * Prefix: 'T' followed with a capital letter (first character of the event handler). | ||
Event Arguments: | Event Arguments: | ||
* Name event argument classes with the <code>EventArgs</code> suffix. | * Name event argument classes with the <code>EventArgs</code> suffix. | ||
* Consider naming events using a verb. | * Consider naming events using a verb. | ||
* Use | * Use Pascal Casing. | ||
* Prefix: 'T' | * Prefix: 'T' followed with a capital letter (first character of the event argument class). | ||
===Property Names=== | ===Property Names=== | ||
Line 812: | Line 767: | ||
* Name properties using nouns or noun phrases. | * Name properties using nouns or noun phrases. | ||
* Consider naming a property with the same name as it’s Type. | * Consider naming a property with the same name as it’s Type. | ||
* Use | * Avoid having two property names that are singular and plural in one Class in order to disambiguate them (also if they are of different Types). | ||
* Prefix: | ** This good practice helps in preventing the programmer who reads the code from accidentally mixing up those two properties. | ||
** Example: Rather than declaring two string properties '<code>FamilyName</code>' and '<code>FamilyName''s''</code>', which are different by only one letter, declare a string property '<code>FamilyName</code>' and '<code>SeveralFamilyNames</code>', which are more different. | |||
* Use Pascal Casing. | |||
* Prefix: none! | |||
===Method Names=== | ===Method Names=== | ||
* Name methods with verbs or verb phrases. | * Name methods with verbs or verb phrases. | ||
* Use | * Use Pascal Casing. | ||
* Prefix: | * Prefix: none! | ||
===Argument Names=== | ===Argument Names=== | ||
'Arguments' are | 'Arguments' are parameters that are passed to a method. They are used much like private variables of a method. | ||
* Use descriptive names | * Use accurate and descriptive names that are sufficient to determine the argument's meaning and it’s type. | ||
* Use | * Use Pascal Casing. | ||
* Prefix: 'A' (stands for '''A'''rgument) | * Prefix: 'A' (stands for '''A'''rgument) followed by a capital letter (first character of the argument name). | ||
===Field Names=== | ===Field Names=== | ||
'Fields' are private variables of a class. | 'Fields' are private variables of a class. | ||
* Use descriptive names | * Use accurate and descriptive names that are sufficient to determine the field's meaning and it’s type. | ||
* Use | * Avoid having two field names that are singular and plural in one Class in order to disambiguate them (also if they are of different Types). | ||
* Prefix: 'F' (stands for '''F'''ield) | ** This good practice helps in preventing the programmer who reads the code from accidentally mixing up those two fields. | ||
** Example: Rather than declaring two string fields '<code>FFamilyName</code>' and '<code>FFamilyName''s''</code>', which are different by only one letter, declare a string field '<code>FFamilyName</code>' and '<code>FSeveralFamilyNames</code>', which are more different. | |||
* Use Pascal Casing. | |||
* Prefix: 'F' (stands for '''F'''ield) followed by a capital letter (first character of the field name). | |||
=== Variable Names=== | === Variable Names=== | ||
'Variables' are private variables of a method. | 'Variables' are private variables of a method. | ||
* Use descriptive names | * Use accurate and descriptive names that should be sufficient to determine the variable's meaning and it’s type. | ||
* Boolean | * Boolean variables: use prefixes like 'Is...', 'Has...' or 'Can...'. and names that imply true or false. For example: 'FileFound', 'Done', 'Success' or with their prefixes: 'IsFileFound', 'IsDone', 'IsSuccess'; don't try 'Is''Name''' that doesn't make sense at all. | ||
* Loop counters: | * Loop counters: use ''Counter'' as the standard name for these kinds of variables as they are used in loops to serve as counters, indexes, or the like. Whenever you have a nested loop that also uses a counter, add numbers to the end to indicate the level of the counter. | ||
* Temporary | * Temporary variables: should be called ''Tmp''. Many times you need to use a variables with no specific meaning - just a quick place-holder or something that will have but a moment of usage. Whenever you need more than one temporary variable in a particular context, you should consider using self-explanatory names for these variables instead. | ||
* Use | * Avoid having two variable names that are singular and plural in one Method in order to disambiguate them (also if they are of different Types). It is also a good practice do this if one method calls another method and the one handles singular and the other one plural instances of something. | ||
* Prefix: | ** These good practices help in preventing the programmer who reads the code from accidentally mixing up those two variables. | ||
** Example: Rather than declaring two string variables '<code>FamilyName</code>' and '<code>FamilyName''s''</code>', which are different by only one letter, declare a string variable '<code>FamilyName</code>' and '<code>SeveralFamilyNames</code>', which are more different. | |||
* Use Pascal Casing. | |||
* Prefix: none! | |||
=== | ===Constant Names=== | ||
* Name constant fields with nouns, noun phrases or abbreviations for nouns. If they consist of more than one word, separate the words with underscore characters (<code>_</code>). | * Name constant fields with nouns, noun phrases or abbreviations for nouns. If they consist of more than one word, separate the words with underscore characters (<code>_</code>). | ||
* Use ALL-UPPERCASE | * Use ALL-UPPERCASE | ||
* Prefix: | * Prefix: none! | ||
For example: | For example: | ||
public class TMath | |||
public class TMath | { | ||
{ | public const string LONG_CONST_NAME = ... | ||
public const double PI = 3.14... | |||
public double Pi = 3.14... <font color="gray">// this is a variable and not a constant, | |||
// so don't use PI</font> | |||
} | |||
} | |||
===Naming and Capitalisation | ===Naming and Capitalisation Summary=== | ||
{| width="643" border="1" cellpadding="4" | {| width="643" border="1" cellpadding="4" | ||
Line 1,006: | Line 968: | ||
|} | |} | ||
=Programming Practices= | =Programming Practices= | ||
Line 1,028: | Line 988: | ||
Instead declare a const which contains the number: | Instead declare a const which contains the number: | ||
public class TMyMath | |||
public class TMyMath | { | ||
{ | public const double PI = 3.14159265358979323846264338 <font color="peach">// As a double, will only store to 15 significant digits</font> | ||
} | |||
</ | |||
== SQL Queries == | == SQL Queries == | ||
Line 1,051: | Line 1,009: | ||
<br /> Cumulative examples: <br /> | <br /> Cumulative examples: <br /> | ||
SELECT a_column_1_i, a_column_2_l, | |||
a_column_3_i, a_column_4_n | |||
FROM a_table | |||
WHERE a_column_1_i > 5 | |||
AND a_column_2_l = TRUE | |||
OR a_column_4_n = 10 | |||
ORDER BY a_column_3_i; | |||
SELECT surname, forenames | |||
FROM employee | |||
WHERE depno = | |||
(SELECT depno | |||
FROM employee | |||
WHERE empno = 16) | |||
AND empno != 16 | |||
DELETE FROM a_whatever | |||
WHERE mmm | |||
AND mmm | |||
OR mmm | |||
UPDATE a_whatever | |||
SET (mm1 = 'khasg kvjshdfbv jszhdvb kszjhvb ksjvhb asj,f abv,jfhv badfj', | |||
mm2 = (SELECT id | |||
FROM something | |||
WHERE something else) | |||
mm3, mm4) | |||
WHERE mmm | |||
AND mmm; | |||
<br /> Table aliases can be named by using the first letter of every word in the table name (excluding unnecessary things like prefixes). If the same table is used twice in the query with two aliases, simply add a number at the end of the alias name to distinguish them, or name them in such a way as to separate them logically. <br /> | <br /> Table aliases can be named by using the first letter of every word in the table name (excluding unnecessary things like prefixes). If the same table is used twice in the query with two aliases, simply add a number at the end of the alias name to distinguish them, or name them in such a way as to separate them logically. <br /> | ||
<span style="font-style: italic;">Never use </span>''SELECT * ''<span style="font-style: italic;">for a query in Petra!</span> This can cause a lot of data transfer! By not using it you can prevent errors, and also make life much easier for things like | <span style="font-style: italic;">Never use </span>''SELECT * ''<span style="font-style: italic;">for a query in Petra!</span> This can cause a lot of data transfer! By not using it you can prevent errors, and also make life much easier for things like Typed DataSets. | ||
= Use Discretion! = | = Use Discretion! = | ||
Line 1,101: | Line 1,054: | ||
==Brace placement example== | ==Brace placement example== | ||
namespace ShowMeTheBracket | |||
namespace ShowMeTheBracket | { | ||
{ | public enum TTheTestEnum | ||
{ | |||
ttOne, ttTwo | |||
} | |||
public class TTestMeClass | |||
{ | |||
TTheTestEnum FTestVar; | |||
public TTheTestEnum Test | |||
{ | |||
get | |||
{ | |||
return FTestVar; | |||
} | |||
set | |||
{ | |||
FTestVar = value; | |||
} | |||
} | |||
void DoSomething() | |||
{ | |||
if (FTestVar == TTestEnum.ttOne) | |||
{ | |||
//...stuff gets done | |||
} | |||
else | |||
{ | |||
//...other stuff gets done | |||
} | |||
} | |||
} | |||
} | |||
} | |||
==Variable naming example== | ==Variable naming example== | ||
Line 1,148: | Line 1,099: | ||
Instead of: | Instead of: | ||
for (int i = 1; i < num; ++i) | |||
for (int i = 1; i < num; ++i) | { | ||
{ | meetsCriteria[i] = true; | ||
} | |||
} | |||
for (int i = 2; i < num / 2; ++i) | |||
for (int i = 2; i < num / 2; ++i) | { | ||
{ | int j = i + i; | ||
while (j <= num) | |||
{ | |||
meetsCriteria[j] = false; | |||
j += i; | |||
} | |||
} | |||
} | |||
for (int i = 0; i < num; ++i) | |||
for (int i = 0; i < num; ++i) | { | ||
{ | if (meetsCriteria[i]) | ||
{ | |||
Console.WriteLine(i + " meets criteria"); | |||
} | |||
} | |||
} | |||
...do name variables appropriately: | ...do name variables appropriately: | ||
for (int Counter1 = 1; Counter1 < num; ++Counter1) | |||
for (int Counter1 = 1; Counter1 < num; ++Counter1) | { | ||
{ | isPrime[Counter1] = true; | ||
} | |||
} | |||
for (int Counter2 = 2; Counter2 < num / 2; ++Counter2) | |||
for (int Counter2 = 2; Counter2 < num / 2; ++Counter2) | { | ||
{ | int factorableNumber = Counter2 + Counter2; | ||
while (factorableNumber <= num) | |||
{ | |||
isPrime[factorableNumber] = false; | |||
factorableNumber += Counter2; | |||
} | |||
} | |||
} | |||
for (int Counter3 = 0; Counter3 < num; ++Counter3) | |||
for (int Counter3 = 0; Counter3 < num; ++Counter3) | { | ||
{ | if (isPrime[Counter3]) | ||
{ | |||
Console.WriteLine(Counter3 + " is prime."); | |||
} | |||
} | |||
} | |||
'''Note:''' | '''Note:''' indexer variables should generally be called Counter1, Counter2, etc. But in cases like this where it isn't quickly obvious what is going on with those loops, it may make sense to reconsider this rule: for instance, <code>Counter1</code> and <code>Counter3</code> could be renamed to <code>PrimeCandidate</code>, and <code>Counter2</code> to <code>Factor</code> to make the code more 'self-documenting'. |
Latest revision as of 10:19, 17 August 2016
About the C# Coding Style Guide
This document may be read as a guide to writing robust and reliable C# programs.
Notes:
- Many of the rules in this document can be applied automatically to source code using the 'Uncrustify' tool that we use (
make uncrustify
)- Those rules are marked with 'Enforced by Uncrustify.'
- Many of the rules in this document can be checked automatically with the 'StyleCop' tool that we use (
make stylecop
)!- Those rules are marked with 'Checked by StyleCop.' TODO
- 'Uncrustify' and 'StyleCop' and how we use them with Petra are described in Coding Standards Tools: 'Uncrustify' and 'StyleCop'.
This 'C# Coding Style Guide' was inspired by the C# Coding Style Guide written by Mike Krüger. This is used by the SharpDevelop programmers who work on the SharpDevelop IDE.
File Organization
C# Sourcefiles
- Keep your classes/files short: try hard not to exceed 3.000 lines of code and never go beyond 4.000 lines of code.
- Exception to that:
- Auto-generated code can ignore that rule if the generated file doesn't need to be looked at/touched by developers.
- Exception to that:
- Divide your code up and make structures clearer.
- Every class should go in a separate file.
- Exceptions to that:
- Helper classes for the main class in the same file are allowed, but they must be private classes!
- Auto-generated code can ignore that rule if the generated file doesn't need to be looked at/touched by developers.
- Exceptions to that:
- The file name should reflect the name of the class in the file (.cs as extension of course).
- Exception to that:
- The file name of a source file can have the last part of a Namespace in it if the Namespace is nested quite deeply and
- Exception to that:
the directory structure doesn't reflect that deep nesting anymore (see 'Directory Layout' below). Example: DataAggregates.PPartnerAddress.cs in Namespace 'Ict.Petra.Server.MPartner.DataAggregates' in directory 'U:\openpetraorg\csharp\ICT\Petra\Server\lib\MPartner'.
These conventions makes things much easier to read and find.
Directory Layout
- Create a directory for every namespace. Example: for Namespace 'Ict.Petra.Server.App.Core' use 'U:\delphi.net\ICT\Petra\Server\app\' as the path (do not use the Namespace name with dots for directory names).
- Exceptions to that:
- Additional directories might be placed inbetween parts of the Namespace if that benefits the directory structure and an agreed rule when to do that is followed. Example: Namespace 'Ict.Petra.Server.MPartner.DataAggregates' resides in directory 'U:\delphi.net\ICT\Petra\Server\lib\MPartner'. Note the 'lib' directory, which is inserted.
- If a Namespace is nested quite deeply, we can opt to have the directory structure not reflecting that deep nesting. In that case, the last part of a Namespace should then be part of the file name of a Class (see 'C# Sourcefiles' above).
- Exceptions to that:
See also 'Explanation of Directory Structure and Rules' for more detailed information!
These conventions make it easier to map namespaces to the directory layout.
Indentation
White Spaces
For code indentation, two standards exist in the world of programming: indendation with spaces and indendation with tabs. Some programmers prefer spaces, others prefer tabs.
Here, we define the space character as the standard indentation character. Specifically, we indent everything with four spaces - Enforced by Uncrustify. This applies for the indendation from column 1 to the indendation level of the current code and also to any further indendations from there.
By using spaces, not tabs, we can ensure that our code is indented identically for every developer and in every editor. If we used tabs, the indentation could vary.
Don't use tabs for indentation - use spaces! Enforced by Uncrustify.
Wrapping Lines
When an expression will not fit on a single line, break it up before it reaches more than 150 characters. Enforced by Uncrustify. You might want to break it up before that for readability.
Follow these general principles when breaking up a line:
- Break after a comma.
- Break after an operator.
- Prefer higher-level breaks to lower-level breaks.
- Indent the new line at the standard indentation level, or align the new line with the beginning of the expression at the same level on the previous line.
Example of breaking up method calls:
LongMethodCall(expr1, expr2, expr3, expr4, expr5); // indented at the standard indentation level
or
LongMethodCall(expr1, expr2, expr3, expr4, expr5); // aligned with the beginning of the expression on the previous line
The first is preferred because it results in uniformly formatted code, but if indenting with the second method makes for better reading in a specific case then use the second method.
Examples of breaking an arithmetic expression:
PREFER:
var = a * b / (c - g + f) + 4 * z;
BAD STYLE – AVOID:
var = a * b / (c - g + f) + 4 * z;
The first is preferred, since the break occurs outside the paranthesized expression (higher level rule).
Comments
All comments must be written in English.
Block Comments
Block comments for the purpose of describing Classes, Methods, etc. should be avoided. Instead, use of the ///
comments to give C# standard descriptions is recommended (see 'Documentation Comments' below).
If you wish to use block comments inside Methods, Classes, etc. you should use the following style:
/* The following Algorithm does bla bla bla * and is adapted from the well-known blah blah Algorithm. * An example of this Algorithm can be found on http://www.blahalgorithms.org */
...as this will set off the block visually from code for the (human) reader.
Block comments may be useful in rare cases, refer to the TechNote 'The fine Art of Commenting' for an example.
As a general rule of thumb, the length of a comment should not overly exceed the length of the code it explains as this is an indication of too complicated (and potentially buggy) code.
For commenting out sections of code, use the 'Single Line Comments' (despite their name) to distinguish better between code that has comments attached and code that is commented out.
Single Line Comments
Developers should use the //
(two forward slahes) comment style to "comment out" code (SharpDevelop has a shotcut key for it, Alt+/). It may be used for commenting out sections of code too.
Note on using StyleCop: because we will use StyleCop, you should rather use ////
(four forward slashes) instead of //
when commenting out single lines of code. This helps the tool to not confuse it with a single line comment (which is not commented out code). (Refers to StyleCop Rule SA1512: SingleLineCommentsMustNotBeFollowedByBlankLine.)
Single line code comments must be indented to the indent level when they are used for code documentation. Commented out code should be commented out so that the //
(two slashes) start in column 1. This is to enhance the visibility of commented out code.
Documentation Comments
In the .NET framework, Microsoft has introduced a documentation generation system based on XML comments. These comments are formally single line C# comments containing XML tags. They follow this pattern for single line comments:
/// <summary> /// This class... /// </summary>
Multiline XML comments follow this pattern:
/// <exception cref=”BogusException”> /// This exception gets thrown as soon as a /// Bogus flag gets set. /// </exception>
All lines must be preceded by ///
(three slashes) to be accepted as XML comment lines.
Tags fall into two categories:
- Documentation items
- Formatting/Referencing
The first category contains tags like <summary>
, <param>
or <exception>
.
These tags represent the elements of a program's API which must be documented for the program to be useful to other programmers.
They usually have attributes such as name
or cref
(as demonstrated in the multi line example above) and are
checked by the compiler, so they should be valid.
The latter category governs the layout of the documentation, using tags such as <code>
, <list>
or <para>
.
For a fuller explanation of XML comments see 'CSharp In-Code Documentation with MS XML Tags'. For information on how to produce API-style documentation for C# Projects whose .cs files contain XML Comment Tags, see 'How to produce CSharp API Documentation'.
For information on commenting best practice and further issues related to commenting, see the TechNote 'The fine Art of Commenting'.
End Comments
By 'End Comments' we mean adding a single-line comment after the closing curly brace "}" of a code block to explain what section of code the closing curly braces ends.
We decided this is unnecessary if this information is obvious. In other words, use 'End Comments' only when the meaning of the closing curly brace "}" of a code block is not immediately obvious due to either a very long block, or in the case of many nested blocks.
Declarations
Number of Declarations per Line
Only one declaration per line is allowed. This greatly enhances readabiliy and it encourages commenting.
GOOD STYLE:
int Level; // indentation level int Size; // size of table
Do not put more than one variable or variables of different types on the same line when declaring them.
BAD STYLE - AVOID:
int Var1, Var2; // What is 'Var1'? What does 'Var2' stand for?
Declarations should be followed by an empty line so that they are visually set apart from the rest of the source code. Enforced by Uncrustify.
The above example also demonstrates the drawbacks of non-obvious variable names.
Be clear when naming variables. Using self-explanatory variable names such as IndentLevel
eradicates the need for making explanatory comments.
Initialization
Try to initialize local variables as soon as they are declared.
For example:
string Name = myObject.Name; int Val = time.Hours;
Initialisations should be followed by an empty line so that they are visually set apart from the rest of the source code. Enforced by Uncrustify if the initialisations are part of declarations.
Note: when initializing a Dialog/Modal Form (by using .ShowDialog instead of .Show), use the following construct to be sure the Form is released from memory when it is closed:
using (TOpenFileDialog openFileDialog = new TOpenFileDialog()) { openFileDialog.ShowDialog(); ... } // .NET will release openFileDialog automatically, so we can't forget about it!
Background: forms shown with .ShowDialog are not released from memory, but kept in memory so the caller can call methods after the Form is closed to retrieve parameters. Therefore, you can forget to release the form from memory later.
Class, Interface and Namespace Declarations
For C# classes, interfaces and namespaces, the following formatting rules should be followed:
- Braces
- The opening brace "{" appears in the next line after the declaration statement.
- The closing brace "}" starts a line by itself indented to match its corresponding opening brace.
- The opening brace "{" and the closing brace "}" should be on the same indentation level as the code block construct that owns it.
- Spacing
- A single space character should be placed on each side of the colon that follows the class name.
- If there is a list of types/interfaces that the class inherits from, the items should be separated by a comma followed by a space character.
- Code block separation
- An empty line should preceed and succeed each class, interface or namespace block.
- Exceptions:
- if a class, interface or namespace block begins immediately after another class, interface or namespace block has been started with an opening brace "{": then no empty line should be inserted.
- if a class, interface or namespace block ends and another enclosing code block also ends immediately after the closing brace "}": then no empty line should be inserted.
- Exceptions:
- An empty line should preceed and succeed each class, interface or namespace block.
For example:
namespace MyNamespace { // no empty line class TMySample : TMyClass, IMyInterface { void DoSomething() { } void DoAnotherThing() { } } // empty line goes here class TMyOtherSample : TMySample, IMyOtherInterface { void DoSomething() { } void DoAnotherThing() { } } // no empty line }
For another brace placement example look at section 'Code Examples - Brace placement example'.
Enforced by Uncrustify: all rules
Method Declarations
For method declarations the following formatting rules should be followed:
- Braces
- Class braces placement rules should be applied.
- Spacing
- No space should be put between a method name and the opening parenthesis "(" that starts its parameter list.
- Code block separation
- An empty line should preceed and succeed the statement block (the same applies to other statements that appear as blocks of code).
For example:
// empty line goes here public MySample(int AMyInt) { this.FMyInt = AMyInt; } // empty line goes here void Inc() { ++FMyInt; } // empty line goes here
Enforced by Uncrustify: all rules
Property, Indexer and Event Declarations
For properties, indexers and events, the following formatting rules should be followed:
- Braces
- Class braces placement rules should be applied. Enforced by Uncrustify: all rules, except for 'opening curly bracket must be on a new line' (Uncrustify can't enforce that for EventHandlers).
- Spacing
- For properties where both 'get' and 'set' blocks are present, an empty line should be placed between the 'get' block and the 'set' block.
- For event handlers where both 'add' and 'remove' blocks are present, an empty line should be placed between the 'add' block and the 'remove' block.
- For indexers, use no space between 'this' and '['. Enforced by Uncrustify.
- Code block separation
- An empty line should preceed and succeed the statement block (the same applies to other statements that appear as blocks of code).
- No one-liners
- Always use braces "{" and "}" on their own code lines even if there is only one statement in the getter/setter or adder/remover!
For example:
// empty line goes here public int Amount { get { ... } // empty line goes here set { ... } } // empty line goes here public EventHandler MyCustomEventHandler { add { ... } // empty line goes here remove { ... } } // empty line goes here public this[string index] { get; // empty line goes here set; } // empty line goes here
Simple Statements
Each code line should contain only one statement.
Return Statements
A return
statement should not use outermost parentheses. Enforced by Uncrustify.
GOOD STYLE :
return (n * (n + 1)) / 2;
BAD STYLE - AVOID:
return ((n * (n + 1)) / 2);
Statements With Multiple Conditions
In a situation where more than one condition is specified (eg. in if
, for
or do-while
-statments), please follow these guidelines:
- Place each condition on a separate line.
- Place the && and || that connects the conditions on the same line as the next condition. (See example.)
- If you have a very complicated condition statement, try to align the different conditions. The goal is to make the condition more readable in the end, so you may use your discretion here as well.
Example:
if (((condition1) && (condition2) || (condition3 && condition4)) (&& !condition5)) { ... }
If, if-else, if else-if else Statements
if, if-else
and if else-if else
statements should follow the following formatting rules:
- Braces
- Class braces placement rules should be applied.
- Spacing
- A space character should be put between "if" and the opening parenthesis "(".
- No space character should be put between the opening bracket "(" and the ensuing code statement(s), or between the code statement(s) and the closing bracket ")".
- Code block separation
- An empty line should preceed and succeed the statement block (the same applies to other statements that appear as blocks of code).
- No one-liners
- Always use braces "{" and "}" on their own code lines even if there is only one statement in the condition!
For example:
// empty line goes here if (condition) { DoSomething(); ... } // empty line goes here if (condition) { DoSomething(); ... } else { DoSomethingOther(); ... } // empty line goes here if (condition) { DoSomething(); ... } else if (condition) { DoSomethingOther(); ... } else { DoSomethingOtherAgain(); ... } // empty line goes here
Enforced by Uncrustify: all rules
For / Foreach Statements
For
and foreach
statements should follow the following formatting rules:
- Braces
- Class braces placement rules should be applied.
- Spacing
- A space character should be put between "for"/"foreach" and the opening parenthesis "(".
- No space character should be put between the opening bracket "(" and the ensuing code statement(s), or between the code statement(s) and the closing bracket ")".
for
only- Space characters should be put after the semicolon ";" which separates the initialisation, condition and execution code parts (contained inside the brackets).
- Inter-term spacing rules should be applied (see 'Inter-term spacing' below).
- Code block separation
- An empty line should preceed and succeed the statement block (the same applies to other statements that appear as blocks of code).
- No one-liners
- Always use braces "{" and "}" on their own code lines even if there is only one statement in the loop!
For example:
// empty line goes here for (int Counter = 0; Counter < 5; ++Counter) { ... } // empty line goes here foreach (int MyIterator in IntList) { ... } // empty line goes here
Enforced by Uncrustify: all rules
While/Do-While Statements
while/do
statements should follow the following formatting rules:
- Braces
- Class braces placement rules should be applied.
- Spacing
- A space character should be put between "while" and the opening parenthesis "(".
- No space character should be put between the opening bracket "(" and the condition, or between the condition and the closing bracket ")".
- Code block separation
- An empty line should preceed and succeed the statement block (the same applies to other statements that appear as blocks of code).
- No one-liners
- Always use braces "{" and "}" on their own code lines even if there is only one statement in the loop!
For example:
// empty line goes here while (condition) { ... } // empty line goes here
An empty while should have the following form:
while (condition) { ; }
A do-while statement should have the following form:
// empty line goes here do { ... } while (condition); // empty line goes here
Enforced by Uncrustify: all rules
Switch Statements
A switch
statement should follow the following formatting rules:
- Braces
- Class braces placement rules should be applied.
- Spacing
- A space character should be put between "switch" and the opening parenthesis "(".
- No space character should be put between the opening bracket "(" and the condition, or between the condition and the closing bracket ")".
- Code block separation
- An empty line should preceed and succeed the statement block (the same applies to other statements that appear as blocks of code).
- An empty line should succeed each 'break;' statement.
- Exception: no empty line after the 'break;' statement in the 'default' block.
- Other line breaks:
- the
case x:
condition and its ensuing statement(s), as well as thedefault:
line and its statement(s), should be separated by a line break.
- the
For example:
// empty line goes here switch (condition) { case A: // line break here ... break; // empty line goes here case B: // line break here ... break; // empty line goes here default: // line break here ... break; // NO empty line to follow! } // empty line goes here
Enforced by Uncrustify: all rules
Try-Catch Statements
try-catch
statements should follow these formatting rules:
- Braces
- Class braces placement rules should be applied.
- Code block separation
- An empty line should preceed and succeed the statement block (the same applies to other statements that appear as blocks of code).
- No one-liners
- Always use braces "{" and "}" on their own code lines even if there is only one statement in the respective try/except/finally section!
For example:
// empty line goes here try { ... } catch (Exception) { ... } // empty line goes here
or
// empty line goes here try { ... } catch (ApplicationException ae) { ... } catch (Exception e) { ... } finally { ... } // empty line goes here
Enforced by Uncrustify - all rules, except for:
- 'Code block separation' (Uncrustify can't enforce that);
- 'Opening curly bracket must be on new line' for the 'finally' statement (Uncrustify's rule to enforce that doesn't work).
White Space
Blank Lines
Blank lines improve readability by setting off blocks of code which are in themselves logically related.
Two blank lines should always be used between:
- Logical sections of a source file.
- Class and interface definitions (try one class/interface per file to prevent this case).
One blank line should always be used between:
- Methods. Enforced by Uncrustify
- Properties.
- Code blocks (
if
statements,for/foreach
loops andwhile
loops,switch
blocks andtry/catch
blocks). Enforced by Uncrustify - Local variables in a method and the first statement in the method. Enforced by Uncrustify (although not always reliable)
- Logical sections inside a method to improve readability.
Blank lines should be indented to the correct indent level instead of leaving them blank or, even worse, using another indentation level. This makes the insertion of new statements in these lines much easier.
Inter-term spacing
There should be a single space after a comma or a semicolon.
For example:
TestMethod(a, b, c); // don't use : TestMethod(a,b,c)
Single spaces surround operators (except unary operators like increment (++
) or logical not (!
)).
For example:
a = b; // don't use a=b; for (int Counter = 0; Counter < 10; ++Counter) // don't use 'for (int i=0; i<10; ++i)' // or 'for(int i=0;i<10;++i)'
Enforced by Uncrustify: all rules.
Naming Conventions
All names of all code elements must be written in English.
Capitalization Styles
Pascal Casing
This convention capitalises the first character of each word (as in TestCounter).
Camel Casing
This convention capitalises the first character of each word except the first word (as in testCounter).
All-Uppercase
All-uppercase capitalises all characters of a word. Use all-uppercase only for constant identifiers. (For non-constant identifiers that are commonly used in all-uppercase (eg. URL, PI) use Pascal Casing instead.)
Naming Guidelines
Naming according to the guidelines for 'Hungarian Notation' is considered bad practice.
Hungarian Notation is a defined set of prefixes and postfixes which are applied to names to reflect the type of the variable. This style of naming was widely used in early Windows programming, but is now obsolete or at least should be considered deprecated. Using Hungarian Notation is not allowed if you follow this guide.
Remember: a good variable name describes the semantic not the type.
An exception to this rule is GUI code. All fields and variable names that contain GUI elements like Button should be prefixed with their respective GUI control prefix.
For example:
System.Windows.Forms.Button btnCancel; System.Windows.Forms.TextBox txtName;
Namespace Naming Guidelines
- Namespace names should be nouns or noun phrases.
- Use Pascal Casing.
- Name prefix: none.
Class Naming Guidelines
- Class names should be nouns or noun phrases.
- Use Pascal Casing.
- Name prefix: '
T
' (stands for Type).- Classes that are derived from
System.Windows.Form
should use the prefixTFrm
instead.
- Classes that are derived from
For example:
public class TConnection { ... } public class TFrmAboutPetra { ... }
Interface Naming Guidelines
- Name interfaces with nouns or noun phrases or adjectives describing behaviour.
- Use Pascal Casing.
- Name prefix: '
I
' (stands for Interface).
For example:
public interface IComponent { ... }
Exception Naming Guidelines
- Name Exceptions with nouns or noun phrases.
- Use Pascal Casing.
- Name prefix: '
E
' (stands for Exception). - Name suffix: '
Exception
'. (The suffix is a recommended convention of Microsoft and considered good style by many C# programmers.)
For example:
public class EDBConnectionNotEstablishedException : EOPDBException { ... }
Enum Naming Guidelines
- Name
- Use singular names for enum types. Pascal Casing.
- Use singular names for enum values. camelCasing.
- Prefixes
- Enum type name: 'T'. It is to be followed by a capital letter (first character of the enum name). Enum types should also include the word
Enum
as a suffix to the name. - Enum values: concatenate the letters of each word that make up the enum type name and prefix those in lower case to the enum value.
- Enum type name: 'T'. It is to be followed by a capital letter (first character of the enum name). Enum types should also include the word
For example:
public enum TModuleSwitchEnum { msNone, msPartner, msPersonnel, msFinance } public enum TUIConnectorTypeEnum { uictPartnerKey, uictLocationKey, uictNewPartner }
Event Names
Event Handlers:
- Name event handlers with the
EventHandler
suffix. - Name event handlers that have a timeline concept of pre and post using the present and past tense (eg.
...ChangingEventHandler
and...ChangedEventHandler
). - For generic event handlers use two parameters named
sender
ande
. - Use Pascal Casing.
- Prefix: 'T' followed with a capital letter (first character of the event handler).
Event Arguments:
- Name event argument classes with the
EventArgs
suffix. - Consider naming events using a verb.
- Use Pascal Casing.
- Prefix: 'T' followed with a capital letter (first character of the event argument class).
Property Names
- Name properties using nouns or noun phrases.
- Consider naming a property with the same name as it’s Type.
- Avoid having two property names that are singular and plural in one Class in order to disambiguate them (also if they are of different Types).
- This good practice helps in preventing the programmer who reads the code from accidentally mixing up those two properties.
- Example: Rather than declaring two string properties '
FamilyName
' and 'FamilyNames
', which are different by only one letter, declare a string property 'FamilyName
' and 'SeveralFamilyNames
', which are more different.
- Use Pascal Casing.
- Prefix: none!
Method Names
- Name methods with verbs or verb phrases.
- Use Pascal Casing.
- Prefix: none!
Argument Names
'Arguments' are parameters that are passed to a method. They are used much like private variables of a method.
- Use accurate and descriptive names that are sufficient to determine the argument's meaning and it’s type.
- Use Pascal Casing.
- Prefix: 'A' (stands for Argument) followed by a capital letter (first character of the argument name).
Field Names
'Fields' are private variables of a class.
- Use accurate and descriptive names that are sufficient to determine the field's meaning and it’s type.
- Avoid having two field names that are singular and plural in one Class in order to disambiguate them (also if they are of different Types).
- This good practice helps in preventing the programmer who reads the code from accidentally mixing up those two fields.
- Example: Rather than declaring two string fields '
FFamilyName
' and 'FFamilyNames
', which are different by only one letter, declare a string field 'FFamilyName
' and 'FSeveralFamilyNames
', which are more different.
- Use Pascal Casing.
- Prefix: 'F' (stands for Field) followed by a capital letter (first character of the field name).
Variable Names
'Variables' are private variables of a method.
- Use accurate and descriptive names that should be sufficient to determine the variable's meaning and it’s type.
- Boolean variables: use prefixes like 'Is...', 'Has...' or 'Can...'. and names that imply true or false. For example: 'FileFound', 'Done', 'Success' or with their prefixes: 'IsFileFound', 'IsDone', 'IsSuccess'; don't try 'IsName' that doesn't make sense at all.
- Loop counters: use Counter as the standard name for these kinds of variables as they are used in loops to serve as counters, indexes, or the like. Whenever you have a nested loop that also uses a counter, add numbers to the end to indicate the level of the counter.
- Temporary variables: should be called Tmp. Many times you need to use a variables with no specific meaning - just a quick place-holder or something that will have but a moment of usage. Whenever you need more than one temporary variable in a particular context, you should consider using self-explanatory names for these variables instead.
- Avoid having two variable names that are singular and plural in one Method in order to disambiguate them (also if they are of different Types). It is also a good practice do this if one method calls another method and the one handles singular and the other one plural instances of something.
- These good practices help in preventing the programmer who reads the code from accidentally mixing up those two variables.
- Example: Rather than declaring two string variables '
FamilyName
' and 'FamilyNames
', which are different by only one letter, declare a string variable 'FamilyName
' and 'SeveralFamilyNames
', which are more different.
- Use Pascal Casing.
- Prefix: none!
Constant Names
- Name constant fields with nouns, noun phrases or abbreviations for nouns. If they consist of more than one word, separate the words with underscore characters (
_
). - Use ALL-UPPERCASE
- Prefix: none!
For example:
public class TMath { public const string LONG_CONST_NAME = ... public const double PI = 3.14... public double Pi = 3.14... // this is a variable and not a constant, // so don't use PI }
Naming and Capitalisation Summary
Type |
Prefix |
Case |
Notes |
---|---|---|---|
Namespaces |
none |
Pascal Casing |
|
Classes / Structs |
T |
Pascal Casing |
Special prefix: |
Interfaces | I |
Pascal Casing |
|
Exception Classes |
E |
Pascal Casing |
Suffix: |
Enum Types |
T |
Pascal Casing |
Suffix: |
Enum Values |
see Notes |
Camel Casing |
Prefix: Concatenate the letters of each word that make up the enum type name and prefix those in lower case to the enum value. |
Event Handlers / Event Arguments |
T |
Pascal Casing |
Suffix for Event Handlers: |
Properties |
none |
Pascal Casing |
|
Methods |
none |
Pascal Casing |
|
Arguments |
A |
Pascal Casing |
|
Fields |
F |
Pascal Casing |
|
Local variables |
none |
Pascal Casing |
|
Global Variables |
G |
Pascal Casing |
|
Constants |
none |
All-Uppercase |
Programming Practices
Visibility
Generally
Give a class, interface, event handler, method, field always the least possible visibility.
This means that you should consider giving it private
visibility, and only if that is not enough increase the visibility - but only as much as is needed.
Fields
Do not make fields public - always make them private. Create properties for fields instead. You may use public static fields (or consts) as an exception to this rule, but be very careful with it.
Having appropriate visibility ensures proper use of classes by every programmer and is kind of 'self-documenting' in the way that it tells how the class can be used.
No 'Magic' Numbers
Do not use 'magic' numbers, i.e. place constant numerical values directly into the source code. Replacing these lateron in case of changes (say, your application can now handle 3540 users instead of the 427 users hardcoded into your code in 50 lines scattered troughout your 25000 lines of code) is error-prone and unproductive. Instead declare a const which contains the number:
public class TMyMath { public const double PI = 3.14159265358979323846264338 // As a double, will only store to 15 significant digits }
SQL Queries
All SQL keywords should be in capital letters.
The main query command keyword (e.g. SELECT, UPDATE, INSERT, etc.) should be on the indentation base, and the rest of the text belonging to that query should be indented.
Indentation for SQL queries should be four spaces (like for the rest of the source code).
If the list of columns or tables become to long, it should be split over more than one line. If this is done, the lists should be aligned.
Every keyword that signifies a new 'section' of the query should be on a new line, with everything belonging to that section indented. (See example.)
The keywords AND and OR should also be on a new line, and indented. It is also recommended to try and align the conditions. To do this, simply add a few spaces after the AND and OR keywords until the condition is aligned to the one in the previous line.
Cumulative examples:
SELECT a_column_1_i, a_column_2_l, a_column_3_i, a_column_4_n FROM a_table WHERE a_column_1_i > 5 AND a_column_2_l = TRUE OR a_column_4_n = 10 ORDER BY a_column_3_i; SELECT surname, forenames FROM employee WHERE depno = (SELECT depno FROM employee WHERE empno = 16) AND empno != 16 DELETE FROM a_whatever WHERE mmm AND mmm OR mmm UPDATE a_whatever SET (mm1 = 'khasg kvjshdfbv jszhdvb kszjhvb ksjvhb asj,f abv,jfhv badfj', mm2 = (SELECT id FROM something WHERE something else) mm3, mm4) WHERE mmm AND mmm;
Table aliases can be named by using the first letter of every word in the table name (excluding unnecessary things like prefixes). If the same table is used twice in the query with two aliases, simply add a number at the end of the alias name to distinguish them, or name them in such a way as to separate them logically.
Never use SELECT * for a query in Petra! This can cause a lot of data transfer! By not using it you can prevent errors, and also make life much easier for things like Typed DataSets.
Use Discretion!
These standards were set up to help us as programmers to better maintain the code. Please try to comply to these standards.
We realise that there are times when the standards would actually have a negative effect, or maybe where the standards are not entirely clear. In these situations, please use your discretion to come up with a suitable alternative, and discuss it with the rest of the team before you use it.
Code Examples
Brace placement example
namespace ShowMeTheBracket { public enum TTheTestEnum { ttOne, ttTwo } public class TTestMeClass { TTheTestEnum FTestVar; public TTheTestEnum Test { get { return FTestVar; } set { FTestVar = value; } } void DoSomething() { if (FTestVar == TTestEnum.ttOne) { //...stuff gets done } else { //...other stuff gets done } } } }
Variable naming example
Instead of:
for (int i = 1; i < num; ++i) { meetsCriteria[i] = true; } for (int i = 2; i < num / 2; ++i) { int j = i + i; while (j <= num) { meetsCriteria[j] = false; j += i; } } for (int i = 0; i < num; ++i) { if (meetsCriteria[i]) { Console.WriteLine(i + " meets criteria"); } }
...do name variables appropriately:
for (int Counter1 = 1; Counter1 < num; ++Counter1) { isPrime[Counter1] = true; } for (int Counter2 = 2; Counter2 < num / 2; ++Counter2) { int factorableNumber = Counter2 + Counter2; while (factorableNumber <= num) { isPrime[factorableNumber] = false; factorableNumber += Counter2; } } for (int Counter3 = 0; Counter3 < num; ++Counter3) { if (isPrime[Counter3]) { Console.WriteLine(Counter3 + " is prime."); } }
Note: indexer variables should generally be called Counter1, Counter2, etc. But in cases like this where it isn't quickly obvious what is going on with those loops, it may make sense to reconsider this rule: for instance, Counter1
and Counter3
could be renamed to PrimeCandidate
, and Counter2
to Factor
to make the code more 'self-documenting'.