C# Style Guide

Having well-defined and consistent coding conventions is important for every project, and Godot is no exception to this rule.

This page contains a coding style guide, which is followed by developers of and contributors to Godot itself. As such, it is mainly intended for those who want to contribute to the project, but since the conventions and guidelines mentioned in this article are those most widely adopted by the users of the language, we encourage you to do the same, especially if you do not have such a guide yet.

Bemerkung

This article is by no means an exhaustive guide on how to follow the standard coding conventions or best practices. If you feel unsure of an aspect which is not covered here, please refer to more comprehensive documentation, such as C# Coding Conventions or Framework Design Guidelines.

Sprachspezifisch

Godot currently uses C# version 7.0 in its engine and example source code. So, before we move to a newer version, care must be taken to avoid mixing language features only available in C# 7.1 or later.

For detailed information on C# features in different versions, please see What's New in C#.

Formatierung

Generelle Richtlinien

  • Nutze Line Feed (LF) zum Zeilenumbruch, nicht CRLF oder CR.
  • Benutze ein Line Feed am Ende jeder Datei, mit Ausnahme der csproj Datei.
  • Use UTF-8 encoding without a byte order mark.
  • Es sollten ** 4 Leerzeichen ** anstelle von Tabulatoren zum Einrücken (die als "weiche Tabulatoren" bezeichnet werden) verwendet werden.
  • Wenn eine Zeile länger als 100 Zeichen ist, sollte sie in mehrere Zeilen aufgeteilt werden.

Zeilenumbrüche und Leerzeilen

For a general indentation rule, follow the "Allman Style" which recommends placing the brace associated with a control statement on the next line, indented to the same level:

// Use this style:
if (x > 0)
{
    DoSomething();
}

// NOT this:
if (x > 0) {
    DoSomething();
}

Wie auch immer, es sollten Zeilenumbrüche innerhalb von Klammern vermieden werden:

  • Für einfache Zugriffsfunktionen (Property Accessors).
  • Für einfache Objekte, Arrays oder Collection Initializer.
  • For abstract auto property, indexer, or event declarations.
// You may put the brackets in a single line in following cases:
public interface MyInterface
{
    int MyProperty { get; set; }
}

public class MyClass : ParentClass
{
    public int Value
    {
        get { return 0; }
        set
        {
            ArrayValue = new [] {value};
        }
    }
}

eine leere Zeile einfügen:

  • After a list of using statements.
  • Zwischen Methoden, Eigenschaften und innerer Typen-Deklarationen.
  • Am Ende jeder Datei.

Field and constant declarations can be grouped together according to relevance. In that case, consider inserting a blank line between the groups for easier reading.

Vermeide Leerzeilen einzufügen:

  • Nach {, der öffnenden Klammer.
  • Vor }, der schließenden Klammer.
  • Nach einem Kommentar-Block oder einem einzeiligem Kommentar.
  • Angrenzend an eine weitere Leerzeile.
using System;
using Godot;
                                          // Blank line after `using` list.
public class MyClass
{                                         // No blank line after `{`.
    public enum MyEnum
    {
        Value,
        AnotherValue                      // No blank line before `}`.
    }
                                          // Blank line around inner types.
    public const int SomeConstant = 1;
    public const int AnotherConstant = 2;

    private Vector3 _x;                  // Related constants or fields can be
    private Vector3 _y;                  // grouped together.

    private float _width;
    private float _height;

    public int MyProperty { get; set; }
                                          // Blank line around properties.
    public void MyMethod()
    {
        // Some comment.
        AnotherMethod();                  // No blank line after a comment.
    }
                                          // Blank line around methods.
    public void AnotherMethod()
    {
    }
}

Verwendung von Leerzeichen

Leerzeichen einfügen:

  • Um binäre und tertiäre Operatoren.
  • Zwischen einer öffnenden Klammer und if, for, foreach, catch, while, lock oder using Schlüsselwörtern.
  • Before and within a single line accessor block.
  • Between accessors in a single line accessor block.
  • Nach einem Komma das nicht am Ende der Zeile steht.
  • Nach einem Semikolon in einer for Statement.
  • Nach einem Doppelpunkt in einem einzelnen case Statement.
  • Um einem Doppelpunkt in einer Typ Deklaration.
  • Um den Lambda Pfeil.
  • Nach einem einzeiligem Kommentar-Symbol (//) und bevor es am Ende einer Zeile genutzt wird.

Nutze kein Leerzeichen:

  • After type cast parentheses.
  • Within single line initializer braces.

The following example shows a proper use of spaces, according to some of the above mentioned conventions:

public class MyClass<A, B> : Parent<A, B>
{
    public float MyProperty { get; set; }

    public float AnotherProperty
    {
        get { return MyProperty; }
    }

    public void MyMethod()
    {
        int[] values = {1, 2, 3, 4}; // No space within initializer brackets.
        int sum = 0;

        // Single line comment.
        for (int i = 0; i < values.Length; i++)
        {
            switch (i)
            {
                case 3: return;
                default:
                    sum += i > 2 ? 0 : 1;
                    break;
            }
        }

        i += (int)MyProperty; // No space after a type cast.
    }
}

Benennungsrichtlinien

Use PascalCase for all namespaces, type names and member level identifiers (i.e. methods, properties, constants, events), except for private fields:

namespace ExampleProject
{
    public class PlayerCharacter
    {
        public const float DefaultSpeed = 10f;

        public float CurrentSpeed { get; set; }

        protected int HitPoints;

        private void CalculateWeaponDamage()
        {
        }
    }
}

Use camelCase for all other identifiers (i.e. local variables, method arguments), and use an underscore (_) as a prefix for private fields (but not for methods or properties, as explained above):

private Vector3 _aimingAt; // Use a `_` prefix for private fields.

private void Attack(float attackStrength)
{
    Enemy targetFound = FindTarget(_aimingAt);

    targetFound?.Hit(attackStrength);
}

There's an exception with acronyms which consist of two letters, like UI, which should be written in uppercase letters where PascalCase would be expected, and in lowercase letters otherwise.

Note that id is not an acronym, so it should be treated as a normal identifier:

public string Id { get; }

public UIManager UI
{
    get { return uiManager; }
}

It is generally discouraged to use a type name as a prefix of an identifier, like string strText or float fPower, for example. An exception is made, however, for interfaces, which should, in fact, have an uppercase letter I prefixed to their names, like IInventoryHolder or IDamageable.

Lastly, consider choosing descriptive names and do not try to shorten them too much if it affects readability.

For instance, if you want to write code to find a nearby enemy and hit it with a weapon, prefer:

FindNearbyEnemy()?.Damage(weaponDamage);

besser als:

FindNode()?.Change(wpnDmg);

Mitgliedsvariablen

Deklarieren Sie keine Mitgliedsvariablen, wenn sie nur lokal in einer Methode verwendet werden, da dies das Lesen des Codes erschwert. Deklarieren Sie sie stattdessen als lokale Variablen in der Methode.

Lokale Variablen

Deklarieren Sie lokale Variablen so nah wie möglich am Platz ihrer ersten Verwendung. Dies erleichtert das Lesen des Codes, ohne zu viel scrollen zu müssen, um herauszufinden, wo die Variable deklariert wurde.

Implicitly typed local variables

Consider using implicitly typing (var) for declaration of a local variable, but do so only when the type is evident from the right side of the assignment:

// You can use `var` for these cases:

var direction = new Vector2(1, 0);

var value = (int)speed;

var text = "Some value";

for (var i = 0; i < 10; i++)
{
}

// But not for these:

var value = GetValue();

var velocity = direction * 1.5;

// It's generally a better idea to use explicit typing for numeric values, especially with
// the existence of the `real_t` alias in Godot, which can either be double or float
// depending on the build configuration.

var value = 1.5;

Andere Überlegungen

  • Use explicit access modifiers.
  • Use properties instead of non-private fields.
  • Use modifiers in this order: public/protected/private/internal/virtual/override/abstract/new/static/readonly.
  • Avoid using fully-qualified names or this. prefix for members when it's not necessary.
  • Remove unused using statements and unnecessary parentheses.
  • Consider omitting the default initial value for a type.
  • Consider using null-conditional operators or type initializers to make the code more compact.
  • Use safe cast when there is a possibility of the value being a different type, and use direct cast otherwise.