Related Jira Issues

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

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

Encoding

Files

Naming

Type Naming

Methods and Parameters

Readability Examples

Common Abbreviations

Spacing

Wrapping

Comments

Usings

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

  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

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

Interfaces

  public interface IThingAgent
  {
      string operationDescription { get; }
      float scale { get; }

Methods

      bool DoThing(string propertyDescription, int spinCount);
  }

Classes

class Example
    {

Fields

static readonly Vector3 k_DefaultLength = new Vector3(1, 2, 3);                             // | When it enhances readability, you can try to column-align blocks of variable definitions at symbol name and assignment tab stops. This is not a hard rule.
        const int               k_MaxCount      = DisplayData.MaxItems;
        static int              s_SharedCount;                                                      // | Note no "= 0". All memory is zero'd out by default, so do not redundantly assign.
        int                     m_CurrentCount;

        public const int totalCount = 123;                                                          // | In the UnityEngine and UnityEditor namespaces (old conventions), public fields are camelCase with no prefix  [FDG Exception]
                                                                                                    // | In the Unity namespace, public fields are PascalCase with no prefix

        public string defaultName { get { return Environment.MachineName; } }                       // | In the UnityEngine and UnityEditor namespaces (old conventions), public properties are camelCase with no prefix  [FDG Exception]
                                                                                                    // | In the Unity namespace, public properties are PascalCase with no prefix

        [Example]                                                                                   // | Drop 'Attribute' postfix when applying an attribute
        public int currentCount
        {
            get { return m_CurrentCount; }                                                          // | Getters are always trivial and do not mutate state (this includes first-run cached results); use a full method if you want to do calculations or caching
            set { m_CurrentCount = value; }
        }                                                                                           // | Put exactly one blank line between multi-line methods and properties

        public string description
        {
            get                                                                                     // | For multiline method bodies, the 'get' and 'set' keywords must be on their own line
            {
                return string.Format(
                    "shared: {0}\ncurrent: {1}\n",
                    s_SharedCount, m_CurrentCount);
            }
        }

Events

public event Action<ThingHappenedEventArgs> thingHappened;

        [Description("I do things"), DebuggerNonUserCode]                                           // | Attributes always go on a line separate from what they apply to (unless a parameter), and joining them is encouraged if they are short
        public void DoThings(IEnumerable<IThingAgent> thingsToDo, string propertyDescription)       // | For types that are already internal (like class Example), use public instead of internal for members and nested types
        {
            List<IThingAgent> doneThings = new List<IThingAgent>();                                 
            string indent = new string(' ', 4);                                                        
                                                                                                    // | When appropriate, separate code blocks by a single empty line
            IList<string> doneDescriptions = new List<string>();                                    

            foreach (IThingAgent thingToDo in thingsToDo)                                                   
            {
                if (!thingToDo.DoThing(propertyDescription, m_CurrentCount))
                    break;                                                                          // | Braces not required for single statements under if or else, but that single statement must be on its own line

                using (File.CreateText(@"path\to\something.txt"))                                   // | Use @"" style string literal for paths with backslashes and regular expression patterns
                using (new ComputeBuffer(10, 20))                                                   // | Don't use braces for directly nested using's
                {                                                                                   // | Braces required for deepest level of nested using's
                    doneThings.Add(thingToDo);
                }
            }

            foreach (IThingAgent doneThing in doneThings)                                           // | Dirty details about allocs at https://q.unity3d.com/questions/1465/when-does-using-foreach-in-c-cause-an-allocation.html
            {                                                                                       // | Braces are required for loops (foreach, for, while, do) as well as 'fixed' and 'lock'
                doneDescriptions.Add(doneThing.operationDescription);
                Debug.Log(indent + "Doing thing: " + doneThing.operationDescription);               // | Prefer a + b + c over string.Concat(a, b, c)
            }

            Debug.Log("System Object is " + typeof(object));                                        // | Always use lowercase `object` for the System.Object class.
            Debug.Log("Unity Object is " + typeof(UnityEngine.Object));                             // | Always use a fully qualified name for Unity's Object type, and never 'Object'
        }

        public void ControlFlow(string message, object someFoo, WindingOrder windingOrder)          // | Use c# aliases of System types (e.g. object instead of Object, float instead of Single, etc.)
        {
            for (int i = 0; i < k_MaxCount; ++i)                                                    // | Using i and j for trivial local iterators is encouraged
            {
                // all of this is nonsense, and is just meant to demonstrate formatting             // | Place comments about multiple lines of code directly above them, with one empty line above the comment to visually group it with its code
                if ((i % -3) - 1 == 0)                                                              // | Wrap parens around subexpressions is optional but recommended to make operator precedence clear
                {
                    ++m_CurrentCount;
                    s_SharedCount *= (int)k_DefaultLength.x + totalCount;

                    do                                                                              // | 'while', 'do', 'for', 'foreach', 'switch' are always on a separate line from the code block they control
                    {
                        i += s_SharedCount;
                    }
                    while (i < m_CurrentCount);
                }
                else                                                                                // | 'else' always at same indentation level as its 'if'
                {
                    Debug.LogWarning("Skipping over " + i);                                         // | Drop 'ToString()' when not required by compiler
                    goto skip;                                                                      // | Goto's not necessarily considered harmful, not disallowed, but should be scrutinized for utility before usage
                }
            }

        skip:                                                                                       // | Goto label targets un-indented from parent scope one tab stop

            // more nonsense code for demo purposes
            switch (windingOrder)
            {
                case WindingOrder.Clockwise:                                                        // | Case labels indented under switch
                case WindingOrder.CounterClockwise:                                                 // | Braces optional if not needed for scope (but note indentation of braces and contents)
                    if (s_SharedCount == DisplayData.MaxItems)                                      // | Constants go on the right in comparisons (do not follow 'yoda' style)
                    {
                        string warningDetails = someFoo.ToString();                                    
                        for (int i = 0; i < s_SharedCount; ++i)
                        {
                            Debug.LogWarning("Spinning a " + warningDetails);
                        }
                    }
                    break;                                                                          // | 'break' inside case braces, if any

                case WindingOrder.Charm:
                    Debug.LogWarning("Check quark");                                                // | Indentation is the same, with or without scope braces
                    break;

                case WindingOrder.Singularity:
                {
                    string warningDetails = message;                                                // | (this seemingly pointless variable is here solely to require braces on the case statements and show the required formatting)

                    if (message == Registry.ClassesRoot.ToString())
                    {
                        // Already correct so we don't need to do anything here                     // | Empty blocks should (a) only be used when it helps readability, (b) always use empty braces (never a standalone semicolon), and (c) be commented as to why the empty block is there
                    }
                    else if (m_CurrentCount > 3)
                    {
                        if (s_SharedCount < 10)                                                     // | Braces can only be omitted at the deepest level of nested code
                            Debug.LogWarning("Singularity! (" + warningDetails + ")");
                    }
                    else if (s_SharedCount > 5)                                                     // | 'else if' always on same line together
                        throw new IndexOutOfRangeException();
                    else if ((s_SharedCount > 7 && m_CurrentCount != 0) || message == null)         // | Always wrap subexpressions in parens when peer precedence is close enough to be ambiguous (e.g. && and || are commonly confused)
                        throw new NotImplementedException();

                    break;
                }

                default:
                    throw new InvalidOperationException("What's a " + windingOrder + "?");
            }
        }

Parameterized Types

Structs

struct MethodQuery
    {
        public string            name { get; set; }
        public IEnumerable<Type> paramTypes { get; set; }
        public Type              returnType { get; set; }

        public override string ToString()                                                           // | Methods generally are not permitted in structs, with exceptions like this noted in the data-oriented programming guidelines.
        {
            var paramTypeNames = paramTypes                                                         // | Prefer fluent function call syntax over LINQ syntax (i.e. y.Select(x => z) instead of 'from x in y select z')
                .Select(p => p.ToString())                                                          // | Prefer breaking long fluent operator chains into one line per operator
                .Where(p => p.Length > 2)
                .OrderBy(p => p[0])
                .ToArray();

            return string.Format(
                "{0} {1}({2})",
                returnType, name, string.Join(", ", paramTypeNames));
        }
    }

EventArgs

struct ThingHappenedEventArgs
    {
        public string thingThatHappened { get; }

        public ThingHappenedEventArgs(string thingThatHappened)
        {
            this.thingThatHappened = thingThatHappened;
        }
    }

Attributes

[AttributeUsage(AttributeTargets.Property)]
    public class ExampleAttribute : Attribute
    {                                                                                               // | Empty types have braces on their own lines
    }

Exceptions

public class ExampleException : Exception
    {
        public ExampleException() {}
        public ExampleException(string message) : base(message) {}
        public ExampleException(string message, Exception innerException) : base(message, innerException) {}
    }
}