C# coding style for Unity
The Unity Engine uses C# for scripting. But even if official code conventions for C# applies, the Unity API also has its own standards. And we have some custom rules on top of that.
In this guide, we try to give as much details as possible, and especially explain why we did that choice.
Script example
This script example follows our code style guidelines for C# in Unity. See details below.
using System.Collections.Generic;
using UnityEngine;
namespace SideXP
{
public class CodeStyleDemo<T> : MonoBehaviour
where T : Object
{
// NEsted classes are allowed only if private or internal
private class NestedClass { }
// Enums are prefixed with "E-"
private enum EObjectType
{
GameObject,
ScriptableObject,
}
// Flag fields are prefixed with "F-"
[System.Flags]
private enum EPlayerStatus
{
None = 0,
Paralyzed = 1 << 0,
Confused = 1 << 1,
Sleeping = 1 << 2,
All = Paralyzed | Confused | Sleeping,
}
// Delegates must have the "-Delegate" suffix
public delegate void PlayerHitDelegate();
// Events must have the "On-" prefix (or "_on" if private)
public event PlayerHitDelegate OnPlayerHit;
// Public variable
public bool followsGuidelines = true;
// Private variable (serialized or not)
private List<T> _objectsList = new List<T>();
public void DisplayText()
{
if (followsGuidelines)
Debug.Log("You can remove brackets for if/else statement if ALL the parts have only one instruction");
else
Debug.Log("If one of the parts contains more than one instruction, ALL the parts must have brackets");
}
}
}
Detailed syntax rules
General rules
If not specified below, elements must follow these general rules:
- Use
PascalCasefor naming elements - Any rule that applies to
publicelements also applies toprotectedandprotected internalelements - Any rule that applies to
privateelements also applies tointernalelements - A single
*.csfile should contain only 1 class (only exception forprivatenested classes) - Always have only 1 statement maximum per line
- Always have only 1 declaration maximum per line
- Prefer full names to abbreviations, excepted for common ones used as name such as Id, Url, ...
- For loops and conditions, use inversion to reduce nesting (prefer an early
return,breakorcontinuestatement to check for false conditions instead of diong all operations in true condition) - Use
nameof()instead of plain strings to reference elements when possible - Never use
thisunless absolutely necessary - Don't use
varunless the variable type is obvious or appears in the right side of a=operator - Indent by 4 spaces
- Always place brackets on a new line
- Prefer custom delegates over
Func<>orAction<>if not obvious (since they can be documented as well as their parameters) - It's allowed to remove brackets for condition or loop statements if they contain only one instruction. For conditions though, if at least one part of a
if/else if/elsecombination contains instructions, all the parts must have brackets - Feel free to use "useless" parenthesis or group operations in a ghost brackets block to improve reading and code layout when meaningful
Imports (using statements)
- Always declare imports and aliases outside namespace declaration
- Take care to remove unused imports
- Group using statements by category in the following order:
- System
- Unity (or other high level engine or framework)
- Thrid-party libraries or plugins
- Custom
Example:
using System;
using System.Collections.Generic;
// You can separate categories with a blank line
using UnityEngine;
using UnityEditor;
using DG.Tweening;
using SideXP.Core;
using SideXP.Core.EditorOnly;
Classes & structs
- Always document a class in-code with a summary comment (
/// <summary>), startingg with "Represents..." if applicable - Use nested class only if
privateorinternal, otherwise you must create a new file for it - Add blank line after the first bracket of a class declaration and before that last one
- Avoid using
-Basesuffix for a base class meant to be inherited, unless it'sabstract - Always add suffix
-Attributeif the class inherits fromSystem.Attribute - Always add suffix
-Exceptionif the class inherits fromSystem.Exception
Interfaces
- Always add prefix
I-for interfaces - Always document an interface in-code with a summary comment (
/// <summary>), starting with "Qualifies a class for..." if applicable
Enums
- Always add prefix
E-for regular enumerations - Always add prefix
F-for enumerations marked withSystem.Flags - Always document an enumeration in-code with a summary comment (
/// <summary>) - Prefer use bit shift notation for flag fields, starting with
1 << 0(eg.1 << 1,1 << 2, ...) - Always add a comma after the last item
Note: The
F-suffix is used to clarify if a field can have multiple values, without the need of checking for the enumeration declaration.
Fields
- If
privateorinternal, usecamelCaseprefixed with_ - If
privateandstatic, usecamelCaseprefixed withs_ - If
constant(whatever public or private), usePascalCase - Choose whether all the fields have an initialization value or none of them, but never both (except for events and delegates). It’s allowed to use
defaultas initialization value - If
public, always document using a summary comment (/// <summary>), or with aTooltipAttributein Unity - If
private, we encourage to document it using a summary comment (/// <summary>), but that's not mandatory - Always use
is-orhas-for boolean fields
Note: The
_prefix is meant to check for scope by just reading the syntax. And thes_prefix is meant to clearly state that the owner is the class itself.
Functions
- Use
PascalCase - Use
camelCasefor parameters - Always document using a summary comment (
/// <summary>), and include parameters documentation if not obvious - Use of local functions is allowed
- Always use prefix
Is-,Has-orAny-for functions that just return a boolean
Generic types
- Use only
T-as prefix for generic parameters - When several type parameters are declared, use clear names all prefixed with
T-(eg.TState,TUser, ...). If not related to a specific base type, use an id instead (eg.T0,T1, ...) - Always place a
whereconstraint on a new line
Delegates
- Always document using a summary comment (
/// <summary>) - Always add suffix
-Delegate - Parameters are named using
camelCase
Events
- Always add prefix
On-(or_onifprivate)
new operator
- Prefer using objct initializers instead of setting all
publicfields one by one
Comments
- Limit comment line width to 140 chars
Regions
Use of regions is not mandatory, but strongly encouraged for long scripts (250+ lines), or scripts that implement functions used in several contexts (eg. utility classes).
Regions can be used to group content by access (Public, Private, ...), or by feature.
Additional rules
Custom markers
Feel free to use the following markers in comments:
@todo: Something to check or do later, whether you plan to create an issue for it or just take a note for yourself@note: Additional information about something interesting in your code. Useful to provide information about something unusual or unexpected you had to do to solve a problem@fix: Identify a special case that had to be handled to address an issue