diff --git a/getting_started/scripting/c_sharp/c_sharp_style_guide.rst b/getting_started/scripting/c_sharp/c_sharp_style_guide.rst new file mode 100644 index 000000000..dcb297444 --- /dev/null +++ b/getting_started/scripting/c_sharp/c_sharp_style_guide.rst @@ -0,0 +1,305 @@ +.. _doc_c_sharp_styleguide: + +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 and contributors of 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. + +.. note:: 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 `_. + +Language Specification +---------------------- + +Currently, Godot uses C# version 6.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.0 or +later, such as pattern matching or expression-bodied members inside get/set accessors. + +For detailed information of C# features in different versions, please see +`What's New in C# `_. + +Formatting Conventions +---------------------- + +* If you create a new file, make sure that it uses linefeed (*LF*) characters to break lines, not *CRLF* or *CR*. +* Use UTF-8 encoding without a byte order mark (BOM ). +* Use 4 spaces instead of tabs for indentation (which is referred to as 'soft tabs'). + +Line Breaks and Blank Lines +--------------------------- + +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: + +.. code-block:: csharp + + // Use this style: + if (x > 0) + { + DoSomething(); + } + + // NOT this: + if (x > 0) { + DoSomething(); + } + +However, you may choose to omit line breaks inside brackets, + +* For simple property accessors. +* For simple object, array, or collection initializers. +* For abstract auto property, indexer, or event declarations. + +.. code-block:: csharp + + // 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}; + } + } + } + +Insert a blank line, + +* After *using* statement list. +* Between method, properties, and inner type declarations. + +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. + +Avoid inserting a blank line, + +* After an opening bracket ('{'). +* Before a closing bracket ('}'). +* After a comment block, or a single line comment. +* Adjacent to another blank line. + +.. code-block:: csharp + + 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; + private Vector3 _y; // Related constants or fields can be + // 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() + { + } + } + +Consider breaking a line when it's longer than 100 characters. And it's also a good practice to +insert a line feed (LF) character at the end of a file because some utilities have trouble +recognizing the last line without it (i.e. Linux's *cat* command). + +Using Spaces +------------ + +Insert a space, + +* Around a binary and tertiary operator. +* Between an opening parenthesis and *if*, *for*, *foreach*, *catch*, *while*, *lock* or *using* keywords. +* Before and within a single line accessor block. +* Between accessors in a single line accessor block. +* After a comma. +* After a semi-colon in a *for* statement. +* After a colon in a single line *case* statement. +* Around a colon in a type declaration. +* Around a lambda arrow. +* After a single line comment symbol ('//'), and before it if used at the end of a line. + +Do not use a space, + +* After a type cast parentheses. +* Within single line initializer braces. + +The following example shows a proper use of spaces, according to some of the the above mentioned conventions: + +.. code-block:: csharp + + public class MyClass : Parent + { + 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. + } + } + +Naming Conventions +------------------ + +Use *PascalCase* for all namespaces, type names and member level identifiers (i.e. methods, properties, +constants, events), except for private fields: + +.. code-block:: csharp + + 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 +underscore('_') as a prefix for private fields (but not for methods or properties, as explained above): + +.. code-block:: csharp + + private Vector3 _aimingAt; // Use '_' 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 +upper case letters when used where Pascal case would be expected, and in lower case letters otherwise. + +Note that *'id'* is **not** an acronym, so it should be treated as a normal identifier: + +.. code-block:: csharp + + 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. However, there's an exception about interfaces, in which case they +**should** be named using an upper case *'I'* as a prefix, 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 a code to find a nearby enemy and hit with an weapon, prefer + +.. code-block:: csharp + + FindNearbyEnemy()?.Damage(weaponDamage); + +Rather than, + +.. code-block:: csharp + + FindNode()?.Change(wpnDmg); + +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: + +.. code-block:: csharp + + // 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 'real_t' alias in Godot, which can either be double or float depending + // on the build configuration. + + var value = 1.5; + +Other Considerations +-------------------- + + * 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 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. diff --git a/getting_started/scripting/c_sharp/index.rst b/getting_started/scripting/c_sharp/index.rst index bb80ec32f..57f6d6842 100644 --- a/getting_started/scripting/c_sharp/index.rst +++ b/getting_started/scripting/c_sharp/index.rst @@ -8,5 +8,6 @@ C# c_sharp_basics c_sharp_features c_sharp_differences + c_sharp_style_guide