Programming Style Guide

Related Jira Issues

https://unity3d.atlassian.net/browse/FNSDKEXT-14

https://unity3d.atlassian.net/browse/FNSDKEXT-184

Introduction

For the Windows libraries and the Unity package, Unity’s internal coding standard will be used. This standard is an extension of Microsoft's Framework Design Guidelines, which defines a number of rules not covered by this document, unless it is explicitly excepting a clause from Microsoft’s FDG.

For the Android libraries, Google’s Java Style Guide will be used, and Unity’s style guide will be adapted where appropriate to Java-specific semantics and language constructs.

Definitions

  • camelCase: *words capitalized, except the first

  • PascalCase: all *words capitalized

  • UPPERCASE: all letters in all *words capitalized

*A “word” may only contain letters and numbers (no underscores or other symbols)

Encoding

  • Text file encoding should use UTF8 with no BOM, using LF (unix) line endings

  • Tabs should use 4-wide tab stops, using spaces only (no tab characters)

  • Lines should not contain any trailing whitespace

  • End of files should always include a single newline

Files

  • The style and standard of any imported libraries or external code should be maintained

  • Use PascalCase for file names, typically matching the name of the dominant type in the file (or if none is dominant, use a reasonable category name).

Naming

  • Use PascalCase for all symbol names, except where noted.

  • No ‘Hungarian notation’ or other prefixes, except where noted.

  • Spell words using correct US-English spelling.

  • Use descriptive and accurate names, even if it makes them longer. Favor readability over brevity.

  • Avoid abbreviations when possible unless the abbreviation is commonly accepted.

  • Acronyms are PascalCase, unless they are exactly two letters, in which case they are UPPERCASE. (ex. GetCpuCycles(), IOStream)

  • Do not capitalize each word in so-called closed-form compound words. (Microsoft’s FDG example)

  • Use semantically interesting names rather than language-specific keywords for type names (i.e. GetLength > GetInt).

  • Use a common name, such as value/item/element, rather than repeating the type name, in the rare cases when an identifier has no semantic meaning and the type is not important (i.e. newElements > newInts)

Type Naming

  • Make type names unambiguous across namespaces and problem domains by avoiding common terms or adding a prefix or a suffix. (ex. use 'PhysicsSolver', not 'Solver')

  • Avoid unnecessary prefixes/suffixes, even when other types in the namespace have a prefix (ex. use 'LightEstimationData', not 'ARLightEstimationData', even though 'ARSession' exists)

  • Avoid naming types after terms that represent different concepts in different domains (ex. use 'AndroidInput', not 'Input')

  • Do not use namespaces to enable reusing an existing type name, as resolving conflicts in user code requires using aliases or fully qualified type names.

Methods and Parameters

  • When a method takes one or more arguments that imply a binary condition, consider the following options:

    • If the method name clearly conveys a binary condition, then use a bool argument (e.g., void SetActive(bool active))

    • If the method name conveys a condition that could conceivably have more than two states:

      • You can create multiple named method variants (e.g., void Repaint() and void RepaintImmediately(), T GetComponent() and T GetComponentReadOnly()).

        • (+) Avoids adding new enumerated type to the API surface.

        • (-) Increases number of named methods (and thus number of search points in API reference).

        • (-) API surface will grow if support for new modes is added later.

      • You can create a single method and add an enum for the condition (e.g., void ApplyChanges(InteractionMode mode = InteractionMode.UserAction)).

        • (+) More robust to future support of additional modes.

        • (+) Single named API point communicates common intent, parameter conveys implementation/execution details.

        • (-) Adds more enumerated types to the API, which may not be widely used.

      • In most cases, prefer overloads rather than additional methods with different names (e.g., void GetItems(List<T> items) and void GetItems(T[] items, int length) instead of void GetItemsDynamic(List<T> items) and void GetItemsPreAllocated(T[] items, int length)).

Readability Examples

  • HorizontalAlignment instead of AlignmentHorizontal (more English-readable)

  • CanScrollHorizontally instead of ScrollableX ('x' is somewhat obscure reference to the x axis)

  • DirectionalVector instead of DirVec (unnecessary and use of nonstandard abbreviation)

Common Abbreviations

  • param (parameter)

  • arg (argument)

  • id (identifier)

  • db (database)

  • ok (okay)

Spacing

  • When dealing with whether to put spaces before open parenthesis:

    • If it looks like a function call, no space (function calls, function definitions, typeof(), sizeof())

    • If it opens a scope, add a space (if, while, catch, switch, for, foreach, using, lock, fixed)

  • No spaces immediately inside any parens or brackets (e.g. no 'if ( foo )' or 'x = ( y * z[ 123 ] )')

  • Comma and semicolon spacing as in English ('int a, float b' and 'for (int i = 0; i < 10; ++i)')

  • Exactly one space is required after the // in a C++ style comment.

  • Do not add a space between a unary operator and its operand (!expr, +30, -1.4, i++, --j, &expr, *expr, (int)obj, etc.).

  • Do not add spaces around member access operators (a.b, a->b, etc.).

  • Spaces are required both before and after all other operators (math, assignment, comparison, lambdas, etc.).

Wrapping

  • Wrap code once it gets to around 120 columns wide to keep side-by-side diffs sane (not a hard limit; use your judgment).

  • When necessary, break lines after boolean operators in conditional expressions, after ';' in for-statements, and after ',' in function calls

Comments

  • Documenting the 'why' is far more important than the 'what' or 'how'.

  • Document anything that would surprise another engineer (or yourself in six months when you've forgotten it).

  • /*C-Style comments*/ are not permitted. They are reserved for commenting out big hunks of code locally (never to be committed).

  • No "divider" comments (i.e. long ----- and ////// comments just to break up code).

  • No "category" comments (i.e. // Functions // Private Data // Globals etc.).

  • Only use /// (triple slash) comments if you are writing xmldoc, and never for ordinary comments that you want to stand out

Usings

  • Located at file scope at the top of the file, never within a namespace.

  • Three groups, which are, top to bottom: System, non-System, aliases. Keep each group sorted.

  • Strip unused 'usings' except the 'minimally-required set', which is marked with *required below.

  • Only use aliases when required by the compiler for disambiguation, and not for hiding rarely-used symbols behind a prefix.

  • Always drop explicit namespace qualifications on types when a 'using' can be added (i.e. almost all of the time).

using System; // | Not required, but strongly encouraged using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using Company.BuildSystem; // | Start of non-System group using Microsoft.Win32; using UnityEngine; using Component = UnityEngine.Component; // | Start of aliases group using Debug = UnityEngine.Debug; namespace UnityEditor // | Full contents of namespace indented {

Enums

  • Use a singular type name, and no prefix or suffix (e.g. no E- prefix or -Enum suffix).

  • Constant names should have no prefix or suffix.

  • Do not specify constant values unless absolutely required (e.g. for version-safe protocols - rare).

enum WindingOrder // | Drop redundant access specifiers (leave off 'internal' at file scope) { // | Opening brace is always on its own line at the same level of indentation as its parent Clockwise, // | Code within the braces always indented one tab stop CounterClockwise, Charm, Singularity, // | Trail last element in a list with ',' } // | Closing brace is on its own line at same level of indentation as parent // | Put exactly one blank line between multi-line types

Flags Enums

  • Use a plural type name, and no prefix or suffix (e.g. no E- prefix and no -Flag or -Flags suffix).

  • Constant names should have no prefix or suffix.

  • Use column-aligned bit shift expressions for the constants (instead of 2, 4, 8, etc.

public enum VertexStreams { Position = 1 << 0, Normal = 1 << 1, Tangent = 1 << 2, Color = 1 << 3, UV = 1 << 4, }

Interfaces

  • Name interfaces with adjective phrases, or occasionally with nouns or noun phrases.

    • - Nouns and noun phrases should be used rarely and they might indicate that the type should be an abstract class, and not an interface.

  • Use 'I' prefix to indicate an interface.

  • Ensure that the names differ only by the 'I' prefix on the interface name when you are defining a class-interface pair, where the class is a standard implementation of the interface.

Methods

  • Give methods names that are verbs or verb phrases.

  • Parameter names are camelCase

Classes

  • Name classes and structs with nouns or noun phrases.

  • No prefix on class names (no 'C' or 'S' etc.).

Fields

  • Use prefix + PascalCase for non-public field naming.

    • Prefixes: m_ = instance field, s_ = static readwrite field, k_ = const

    • Also prefix static/instance readonly with k_ if the intent is to treat the field as deeply const.

  • Drop redundant initializers (i.e. no '= 0' on ints, '= null' on ref types, etc.).

  • Never expose public fields which are not const or static readonly. These fields should be published through a property.

  • Use readonly where const isn't possible.

Events

  • Do not declare new delegate types. Use Action<...> instead.

  • Do not expose public delegate fields. Use events instead.

  • Include one participle-form verb in the event name (generally ending in -ed or -ing, ex. occurred, loading, started, given)

  • *EventArgs struct parameters are not necessary, but they should be used if the data sent to the event has the possibility of needing to be changed. [FDG Exception]

Parameterized Types

  • When only a single parameterized type is used, naming it 'T' is acceptable.

  • For more than one parameterized type, use descriptive names prefixed with 'T'.

  • Consider indicating constraints placed on a type parameter in the name of the parameter.

Structs

  • Name classes and structs with nouns or noun phrases.

  • No prefix on class names (no 'C' or 'S' etc.).

  • Structs may be mutable, but consider immutability when appropriate. [FDG Exception]

EventArgs

  • Always use structs for EventArgs types, and never extend System.EventArgs [FDG Exception]

  • Make EventArgs structs immutable

  • See the event example above for when to define EventArgs structs.

Attributes

  • Mark up all attributes with an AttributeUsage, as narrow as possible.

  • Postfix attribute class names with "Attribute".

Exceptions

  • Postfix exception class names with "Exception".

  • Do not inherit from ApplicationException