Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 6 Next »

Related Jira Issues

FNSDKEXT-14 - Getting issue details... STATUS

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 overridden.

For the Android libraries, Google’s Java Style Guide will be used.

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.

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

Methods

  • Give methods names that are verbs or verb phrases.

  • Parameter names are camelCase

      bool DoThing(string propertyDescription, int spinCount);
  }

Classes

  • Name classes and structs with nouns or noun phrases.

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

class Example
    {

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.

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

  • 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]

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

  • 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.

      public static TResult Transmogrify<TResult, TComponent>(                                    // | When wrapping params, do not leave any on line with function name
        TComponent component, Func<TComponent, TResult> converter)                              // | When wrapping, only indent one stop (do not line up with paren)
        where TComponent : Component
        {
            return converter(component);
        }
    }
    

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]

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

  • 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.

struct ThingHappenedEventArgs
    {
        public string thingThatHappened { get; }

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

Attributes

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

  • Postfix attribute class names with "Attribute".

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

Exceptions

  • Postfix exception class names with "Exception".

  • Do not inherit from ApplicationException

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