C# coding Standard
— Code management and collaboration — 2 min read
Here is something I found useful while collaborate with others which creates easy-to-read, high quality codes.
Classes & Interfaces
- Written in PascalCase
- If it's a class that interacts with other classes, use names ending in -er (2)
Example:
1SwitchButton2ButtonSwitcher
Methods
- Written in PascalCase
- Needs to start with a verb e.g. Do, Change, Swap, Process
Example:
1DoSomething()
Fields
- Public fields are written in PascalCase
- Private and protected are written in camelCase with m before them (_ is also ok, but if you consider writting in C++ as well, m is a better practice, because C++ don't accept such typing)
Example:
1public class MyClass {2 public int PublicField;3 private int m_privateField;4 protected int m_protectedField;5}
Parameters
- Parameters are written in camelCase
Example:
1void DoSomething(Vector3 location)
Constants
- Are written with capital letters, separated by underscore
Example:
1private const float FADE_EFFECT_MINIMUM_DISTANCE = 0.075f;
Declarations
Access Level Modifiers
Access level modifiers should be explicitly defined for classes, methods and member variables (public, private, protected).
Note this is usually only an issue for the private modifier, since that field can be omitted.
Example:
1private void Update() {2}
Fields & Variables
- Prefer single declaration per line, sometimes logically grouping fields can be useful but this should be the exception
Usually bad
1string username, twitterHandle;
Usually good
1string username;2string twitterHandle;
Creating a Serializable class to group variables in logic chunks can be useful to reduce clutter in inspector and logically group sets of variables as one unit.
Events
Name events so it's clear when they are invoked: OnTargetReached, OnObjectRotated, OnSessionCompleted.
Notice how it's often useful to name events in the past tense to show exactly when the event is invoked.
Classes
Exactly one class per source file, although inner classes are encouraged where scoping is appropriate.
Interfaces
- All interfaces should be prefaced with the letter I
Bad
1RadialSlider
Good
1IRadialSlider
Spacing
Spacing is especially important in the code, as the code needs to be easily readable for all team members.
Line Length
Lines should be no longer than 130 characters long.
Vertical Spacing
Try to separate variable declaration, method calling and other functionalities in groups inside the same method. If there are many groups, probably they need to be split up in different methods.
Brace Style
Good
1class ABC2{3 void doSomething()4 {5 }6 }
Since this is the brace style in most C# projects and also recommended in Microsoft's own code convention guide.
Bad
1class ABC {2 void DoSomething() {3 }4}
Switch Statements
Try to use the pattern match switch when possible, since it reduces boilerplate and prevents common pitfalls of regular switches like code execution falling through one branch into the next.
1command switch2{3 "SystemTest" => RunDiagnostics(),4 "Start" => StartSystem(),5 "Stop" => StopSystem(),6 "Reset" => ResetToReady(),7 _ => throw new ArgumentException("Invalid string value for command", nameof(command)),8};
1switch (something)2{3 case A:4 break;5 case B:6 break;7}
Code Structure
General:
- Try to separate different functionalities with return
- Try to group different functionalities together. E.g. transforms (position, rotation);
- Try to early exit at top of the function when conditions required for the function to operate are not met
Example
Good
1private float DivideNumbers(float a, float b)2{3 if (b == 0) { return 0; }4 return a / b;5}
Bad
1private float DivideNumbers(float a, float b)2{3 if (b != 0)4 {5 return a / b;6 }7 8 return 0;9}
By using return statements when the code needs to stop, we can prevent right code indentation.
Important notes
Public / Private / HiddenInInspector
In some cases, we might need to expose public variables from the class. Though, we don't want to provide them visibility in the inspector (because this might create confusion such as "should I put something in this empty field?"). In this case, there are 2 ways of solving it:
1[HideInInspector]2public int TheValue;
1public int TheValue { get; set; }2public int TheOtherValue { get; private set; }
The 2nd is preferred. In special cases like this, these two should be kept together and placed with public variables. If you only want to expose a getter the 3rd option is the best.
⚠️ Warning: when serializing JSON classes, some variable declarations do not work, essentially you should think of properties more as methods than variables.
Works:
1public int Number;
Does not serialize:
1public int Number { get; set; }
Attributes
General
Since it's better for cohesiveness and structure if all attributes follow the same convention, opt to put all attributes above the code they target - even if it's a class or a variable. This way there's less clutter in diffs if someone needs to add more attributes, and we don't have to worry about readability or total line length when adding attributes.\ \ Bad
1[SerializeField, HideInInspector] private bool _myField;
Good
1[CustomPropertyDrawer(typeof(MyClass)]2public class MyClassPropertyDrawer : PropertyDrawer3
4[SerializeField, HideInInspector]5private bool _myField;
Class Structure
1/// Description for the class2class ExampleClass3{4
5 // Constants.6 private const float TIME_OF_DELAY_IN_SECONDS = 0.5f;7
8 // Child classes.9 private class KeyPairValue10 {11 public string Key;12 public string Value;13 }14
15 // Child enums.16 private enum OptionsEnum17 {18 OptionOne,19 OptionTwo,20 OptionThree21 }22
23 // Public variables.24 public int Number;25 public int Count;26
27 // Private/protected, which are exposed into the editor.28 [SerializeField]29 private float _secondsToStart;30 [SerializeField]31 protected string _debugText;32
33 // Private/protected, not exposed in the editor.34 private string _translationForName;35
36 // Unity default methods.37 private void Awake()38 {39 }40
41 private void OnEnable()42 {43 }44
45 // Public custom methods.46 public void SetData(string newData)47 {48 }49
50 // Private/protected methods.51 private void CalculateRange()52 {53 }54
55 // Callbacks.56 private void OnButtonPressed()57 {58 }59
60 private void OnEventTriggered()61 {62 }63}
Note: there are exceptions, for example, it is more important that the initialization sequence is one after the other even if some methods are private.