Browse Source

Merge pull request #1394 from mysticfall/master

#1296: Create official Godot C# style guide
Ignacio Etcheverry 7 years ago
parent
commit
7e9bd53ce6

+ 305 - 0
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 <https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions>`_ or
+        `Framework Design Guidelines <https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/naming-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# <https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/>`_.
+
+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 <https://en.wikipedia.org/wiki/Byte_order_mark>).
+* 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' <https://en.wikipedia.org/wiki/Indentation_style#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<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.
+        }
+    }
+
+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.

+ 1 - 0
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