Related Jira Issues
Jira Legacy | ||||||
---|---|---|---|---|---|---|
|
Jira Legacy | ||||
---|---|---|---|---|
| ||||
|
|
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.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
...
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. htmlText, 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)
...
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)).
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)
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)
...
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
...
Code Block | ||
---|---|---|
| ||
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 { var doneThings = new List<IThingAgent>(); types { List<IThingAgent> doneThings = new List<IThingAgent>(); // | 'var' required on any 'new' where the type we want is the same as what is being constructed varstring indent = new string(' ', 4); // | ...even primitive types // | When appropriate, separate code blocks by a single empty line IList<string> doneDescriptions = new List<string>(); // | (This is a case where 'var' not required because the types of the variable vs the ctor are different) foreach (varIThingAgent thingToDo in thingsToDo) in thingsToDo) // | 'var' required in all foreach { 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 (varIThingAgent 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) { varstring warningDetails = someFoo.ToString(); // | 'var' for the result of assignments is optional (either way, good variable naming is most important) for (varint 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: { varstring 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 + "?"); } } |
...
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.
Code Block language c# 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]
...