Syntax
Indent blocks two spaces. Make sure your editor uses spaces only, no tabs.
- Maybe... It looks like at least the Microsoft standard is to put braces on their own lines, and to leave them out for single-line blocks. Should we consider going with this style instead?
- Or... Use the same bracing style as was used
in the Java code branch. Braces are required around every block, even
single-line ones. Opening braces go at the end of function signatures,
ifs,try/catchstatements, etc.
Don't put a space before or after left parentheses,
except for after a language keyword like if or for.
Put one space before left parentheses in that case.
Try to minimize the use of parentheses. You can rely
on operator precedence and associativity to disambiguate most expressions, so
only use parentheses if you need to override the default
precedence/associativity rules. For example, || has lower
precedence than &&, which has lower precedence than
relational operators like == and <=, which in turn
have lower precedence than arithmetic operators. So writing
if (index >= 0 && index < array.Count - 1 || array.isEmpty)
is preferable to
if (((index >= 0) && (index < array.Count - 1)) || (array.isEmpty))
Here's an example demonstrating these guidelines:
try
{
File file = new
File("sample.txt");
String line = null;
while ((line = file.readLine()) !=
null)
output.writeLine(line);
}
catch (IOException exception)
{
}
finally
{
}
C# niceties
C# has a few syntactic niceties over Java. Instead of
getXXX and setXXX methods use properties, like
so:
public int Length {
get { return this.length; }
set { this.length = value; }
}
C# also has enumerations, a feature Java inexplicably
left out. For a list of constants, don't create a series of static final
int variables, or whatever. Instead, do this:
public enum XPathTokenType
{
/// <summary>Left parenthesis
'('.</summary>
LeftParenthesis,
/// <summary>Right parenthesis
')'.</summary>
RightParenthesis,
/// <summary>Left square bracket
'['.</summary>
LeftBracket,
/// <summary>Right square bracket
']'.</summary>
RightBracket,
/// <summary>Current node
'.'.</summary>
Dot,
/// <summary>Parent node
'..'.</summary>
DotDot,
/// <summary>Attribute sign
'@'.</summary>
AttributeSign,
/// <summary>Comma
','.</summary>
Comma,
/// <summary>Axis separator
'::'.</summary>
ColonColon,
...
}
Naming conventions
C# has a slightly different naming convention from that of Java or C++. In C#, all public members should be capitalized—even functions and variables. Private members are distinguished by having names in lower case.
As a rule, don't abbreviate words in names. Always spell out every word completely, even if the name is long.
Boolean variables/properties/functions should usually
begin with is or something similar, such as isEmpty.
This is a standard naming convention to make code read more like English, and
is an easy way to distinguish booleans from other types.
Otherwise, avoid tagging variables with
prefixes/suffixes indicating their type. For array or list variables, instead
of adding "list" or "array" at the end of the variable
name, make it plural. So elements is better than
elementList.
XML Documentation
Please document every class, method, property, and variable—even private ones! Learn and use the C# XML documentation format. It's basically like Javadoc, except everything's marked up with XML tags. The MSDN Library has full documentation of all the tags. Visual Studio will generate a documentation template when you start an XML comment, automatically adding tags for each parameter to a function, for example.
There are several reasons why this is very important to do:
- If you get into the habit of commenting code as you write it, it's an easy CMMI practice to follow.
- Just like Javadoc, we can dump these comments to HTML pages, getting full API documentation for "free".
- Visual Studio understands these comments. It will popup tooltips when you hover over variables/methods/etc. that have been documented.
- If you tag everything well, the compiler will warn you when comments get out of date, like if you rename a variable but don't update the comment.
It's really important to tag every reference to a
variable/function/class/whatever in your comments. You can do this by adding
the cref attribute to any tag, or by using the
<paramref/> tag for parameter names. If you do that, Visual
Studio will create a link for the name, and will also be able to warn you if
you change that name later and forget to update the comment.
Visual Studio has an option you can enable to warn you about uncommented items. Please turn this on! It's always tempting to think we'll go back later after you've finished coding to write all the comments you need to, but we usually never find time to go back and do it. Turning on this option will keep us from slacking off since the warnings will hopefully nag us into commenting sooner rather than later.
Quirks
The documentation for XML comments is a bit inadequate. In lieu of any official guidelines, let's follow these:
- Use the
<c cref="...">code</c>tag to mark short pieces of code like class names, or<see cref="...">code</c>if you want the code to be a link. Note that contrary to what the MSDN Library documentation suggests, the<see/>tag does not automatically add the word "See".
- For variables and properties, use the
<value/>tag instead of<summary/>. Visual Studio will insert a<summary/>tag by default—ignore that and replace it with<value/>.
Formatting
The default style of writing tags on separate lines like
/// <summary>
/// The length of the array.
/// </summary>
seems excessively wasteful of vertical space. I think
it looks better to put the text on the same line as the tag, like people
normally do with <p/> paragraphs in HTML.
/// <summary>Validates the
given XML document and returns an <see cref=
/// "XmlDocument">XmlDocument</c>
containing the document.</summary>
///
/// <param name="xmlReader">A reader
referencing the XML document to validate.
/// </param>
/// <param name="grammarNamespace">The
namespace for grammar
elements.</param>
///
/// <returns>An <c
cref="XmlDocument">XmlDocument</c> containing the
document.
/// </returns>
///
/// <exception
cref="XmlSchemaException">Thrown if
the document does not
/// validate against the
schema.</exception>
/// <exception cref="XmlException">Thrown
if the document is
invalid for a
/// reason not expressed in
the schema.</exception>
private XmlDocument loadDocument(XmlReader
xmlReader, string grammarNamespace)
{
...
}
Assertions and Unit Testing
Assertions
Assertions are a great way to check conditions during run-time that should always be true—things like parameter values being correct, or verying that a variable has the correct value in a complex piece of code.
You should add assertions wherever possible, usually
to verify that a function's parameters are correct. Use the Debug
and Trace classes in System.Diagnostics for this
purpose. The only difference between the two is that Debug is
enabled only for debug builds, whereas Trace is enabled for both
debug and release builds.
using System.Diagnostics;
Boolean IsValidXPath(String xpath)
{
Debug.Assert(xpath != null);
Debug.Assert(xpath.Length > 0);
...
}
Do not use assertions to check for valid user input, or other conditions like that which can fail during the correct operation of the program. Assertions should only be used to verify conditions that absolutely must be true no matter what. If you need to verify things that do not indicate coding errors, such as a file not opening or an input file having errors, throw an exception instead.
Unit Testing
Unit testing involves adding tests for each public
class and method to check that they are fully functional. We will be using the
NUnit package to do our unit
testing.
You should add as many unit tests as you think are necessary for each public method in the program. They should verify that the method does what it is supposed to do. Unit tests are mainly useful during refactoring, when rewriting and reorganizing large chunks of code, to verify that the new code still works.
Unit tests are fully automated, so always run the full unit testing suite after compilation to ensure that nothing is broken.
Important: Whenever you unearth a bug and fix it, document the bug by adding a unit test to exercise that bug. Oftentimes old code has been written to avoid many non-obvious bugs; if those bugs are expressed as unit tests, then we will guarantee that nobody ever accidentally reintroduces that bug again later on when "fixing" old code.
Exceptions
Specific exceptions
Exceptions should be used extensively for error-handling whenever possible. Always use the most specific exception possible. It's a bit cumbersome, but you should always try to create a custom exception class for each type of error. In the long run, the payoff is large.
Along with this, don't ever write catch
(Exception exception). That's too large of a net to cast and will likely
catch exceptions you hadn't planned on catching, hiding errors. It also kills
exception propagation, preventing exceptions from bubbling up
properly.
Instead, catch a more specific exception. There's
nothing wrong with having multiple successive catch blocks to
catch each type of exception you are able to handle.
Catching exceptions
If we do this properly, then we can observe the most important tenet of exception handling: only catch an exception if you can actually deal with it at that point. The reason exceptions are better than old-style error codes is that you don't have to handle errors immediately. You can let somebody higher up in the call chain handle an error if you don't have any particular recourse.
So instead of catching an exception, logging the stack trace, and moving on, just let the exception propagate upwards. It's a good idea to wrap the exception if you can, but you should rethrow it unless you are able to fix whatever problem caused the exception.
Miscellaneous
Distinguish between externally visible classes and
internal FormFaces ones. Use internal for most classes; use
public only for classes that are to be part of the external
FormFaces API.
(This does not apply to methods; if the class is
already internal, you should just make the methods
public. You'd only need to make a method internal if
it belongs to a public class but should only be called from within
FormFaces.)
(Maybe) Always qualify references to class
variables with this. It helps to distinguish between local
variables and class variables in function bodies.
Prefer the uppercase forms of built-in classes to
lowercase ones—Boolean instead of bool,
Object instead of object, etc. However, stick with
int instead of Int32.