Skip to main content

Coding Guidelines

Introduction

As Cocos2D-Mono continues to mature, the goal is maintain a professional and consistent look throughout the source code. As this project mainly started out following the efforts of MonoGame and Cocos2d-Xna it was decided to follow the Microsoft coding guidelines (the default provided in Visual Studio's C# editor) as MonoGame does. These coding guidelines listed below are based on a MSDN blog post from 2005 by Brad Abrams describing the internal coding guidelines at Microsoft, with some changes to suit our project.

Tabs & Indenting

Tab characters (\0x09) should not be used in code. All indentation should be done with 4 space characters.

Bracing

Open braces should always be at the beginning of the line after the statement that begins the block. Contents of the brace should be indented by 4 spaces. Single statements do not have braces. For example:

if (someExpression)
{
DoSomething();
DoAnotherThing();
}
else
DoSomethingElse();

case statements should be indented from the switch statement like this:

switch (someExpression)
{
case 0:
DoSomething();
break;

case 1:
DoSomethingElse();
break;

case 2:
{
int n = 1;
DoAnotherThing(n);
}
break;
}

Braces are not used for single statement blocks immediately following a for, foreach, if, do, etc. The single statement block should always be on the following line and indented by four spaces. This increases code readability and maintainability.

for (int i = 0; i < 100; ++i)
DoSomething(i);

Single line property statements

Single line property statements can have braces that begin and end on the same line. This should only be used for simple property statements. Add a single space before and after the braces.

public class Foo
{
int bar;

public int Bar
{
get { return bar; }
set { bar = value; }
}
}

Commenting

Comments should be used to describe intention, algorithmic overview, and/or logical flow. It would be ideal if, from reading the comments alone, someone other than the author could understand a function's intended behavior and general operation. While there are no minimum comment requirements (and certainly some very small routines need no commenting at all), it is best that most routines have comments reflecting the programmer's intent and approach.

Comments must provide added value or explanation to the code. Simply describing the code is not helpful or useful.

// Wrong
// Set count to 1
count = 1;

// Right
// Set the initial reference count so it isn't cleaned up next frame
count = 1;

Copyright/License notice

Source files do not carry per-file copyright headers. The project's license lives in LICENSE at the root of the engine repository and applies to the whole source tree — don't add a header to new files.

Documentation Comments

All methods should use XML doc comments. For internal dev comments, the <devdoc> tag should be used.

public class Foo
{
/// <summary>Public stuff about the method</summary>
/// <param name="bar">What a neat parameter!</param>
/// <devdoc>Cool internal stuff!</devdoc>
public void MyMethod(int bar)
{
...
}
}

Comment Style

The // (two slashes) style of comment tags should be used in most situations. Wherever possible, place comments above the code instead of beside it. Here are some examples:

// This is required for WebClient to work through the proxy
GlobalProxySelection.Select = new WebProxy("http://itgproxy");

// Create object to access Internet resources
WebClient myClient = new WebClient();

Spacing

Spaces improve readability by decreasing code density. Here are some guidelines for the use of space characters within code:

Do use a single space after a comma between function arguments.

Console.In.Read(myChar, 0, 1); // Right
Console.In.Read(myChar,0,1); // Wrong

Do not use a space after the parenthesis and function arguments.

CreateFoo(myChar, 0, 1) // Right
CreateFoo( myChar, 0, 1 ) // Wrong

Do not use spaces between a function name and parentheses.

CreateFoo() // Right
CreateFoo () // Wrong

Do not use spaces inside brackets.

x = dataArray[index]; // Right
x = dataArray[ index ]; // Wrong

Do use a single space before flow control statements.

while (x == y) // Right
while(x==y) // Wrong

Do use a single space before and after binary operators.

if (x == y) // Right
if (x==y) // Wrong

Do not use a space between a unary operator and the operand.

++i; // Right
++ i; // Wrong

Do not use a space before a semi-colon. Do use a space after a semi-colon if there is more on the same line.

for (int i = 0; i < 100; ++i) // Right
for (int i=0 ; i<100 ; ++i) // Wrong

Naming

Follow all .NET Framework Design Guidelines for both internal and external members. Highlights of these include:

  • Do not use Hungarian notation
  • Do use an underscore prefix for member variables, e.g. "_foo"
  • Do use camelCasing for member variables (first word all lowercase, subsequent words initial uppercase)
  • Do use camelCasing for parameters
  • Do use camelCasing for local variables
  • Do use PascalCasing for function, property, event, and class names (all words initial uppercase)
  • Do prefix interfaces names with "I"
  • Do not prefix enums, classes, or delegates with any letter

The reasons to extend the public rules (no Hungarian, underscore prefix for member variables, etc.) is to produce a consistent source code appearance. In addition, the goal is to have clean, readable source. Code legibility should be a primary goal.

Legacy m_ fields are not free to rename

Older code used an m_ prefix for fields. Private fields have been converted to _camelCase, but several hundred protected fields (plus a handful of public and internal ones) still carry m_ — and those are part of the public API surface that derived types compile against.

Renaming them is a breaking change, tracked as deliberate future work with [Obsolete] shims. Leave them alone in unrelated PRs: rename private fields freely, but don't opportunistically "clean up" a protected m_ field while you're passing through.

File Organization

  • Source files should contain only one public type, although multiple internal types are permitted if required
  • Source files should be given the name of the public type in the file
  • Class members should be grouped logically: fields, properties, constructors, events, methods, explicit interface implementations, then nested types
  • Using statements go before the namespace declaration
  • New files use a file-scoped namespace declaration; most of the source tree has been converted
using System;

namespace MyNamespace;

public class MyClass : IFoo
{
int _foo;

public int Foo { get { ... } set { ... } }

public MyClass()
{
...
}

public event EventHandler FooChanged { add { ... } remove { ... } }

void DoSomething()
{
...
}

void IFoo.DoSomething()
{
DoSomething();
}

class NestedType
{
...
}
}

#region blocks appear in some older files. They're not required for new code — prefer logical ordering and small types — but when editing a file that already uses them, keep its existing structure rather than reorganizing it in an unrelated PR.

Public API stability

Games ship against this library, so public API is changed deliberately rather than opportunistically:

  • Avoid breaking public or protected API. This includes renaming fields and methods, changing parameter types, and removing members — anything a consumer or a derived type compiles against.
  • When a change is genuinely warranted, ship an [Obsolete] shim that keeps the old member working and points at the replacement. The old member is removed in a later major version, not the same one.
  • Breaking changes require a major version bump and migration notes.

This is why cleanup that looks trivial — renaming a legacy m_ field, fixing a casing typo in a public member — is handled as its own tracked, deprecation-cycled change instead of being folded into a passing PR.

Useful Links

C# Coding Conventions (MSDN)