Skip to content
BigBro222's Blog
LinkedInGitHub

C# coding Standard

Code management and collaboration2 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:

1SwitchButton
2ButtonSwitcher

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 ABC
2{
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 switch
2{
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 : PropertyDrawer
3
4[SerializeField, HideInInspector]
5private bool _myField;

Class Structure

1/// Description for the class
2class ExampleClass
3{
4
5 // Constants.
6 private const float TIME_OF_DELAY_IN_SECONDS = 0.5f;
7
8 // Child classes.
9 private class KeyPairValue
10 {
11 public string Key;
12 public string Value;
13 }
14
15 // Child enums.
16 private enum OptionsEnum
17 {
18 OptionOne,
19 OptionTwo,
20 OptionThree
21 }
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.

© 2023 by BigBro222's Blog. All rights reserved.