Guía de estilo de C#

Tener convenciones de codificación bien definidas y consistentes es importante para cada proyecto, y Godot no es una excepción a esta regla.

Esta página contiene una guía de estilo de codificación que es seguida por los desarrolladores y colaboradores de Godot. Como tal, está dirigido principalmente a aquellos que quieren contribuir al proyecto, pero dado que las convenciones y directrices mencionadas en este artículo son las de mayor adopción por parte de los usuarios del lenguaje, te animamos a que hagas lo mismo, especialmente si aún no dispones de dicha guía.

Nota

Este artículo no es de ninguna manera una guía exhaustiva sobre cómo seguir las convenciones de codificación estándar o las mejores prácticas. Si se siente inseguro de un aspecto que no se cubre aquí, por favor refiérase a una documentación más completa, tal como C# Coding Conventions o Framework Design Guidelines.

Especificación del lenguaje

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.

Para obtener más información sobre las características de C# en diferentes versiones, consulta What’s New in C#.

Convenciones de Formato

  • Use line feed (LF) characters to break lines, not CRLF or CR.
  • Use UTF-8 encoding without a byte order mark.
  • Use 4 spaces instead of tabs for indentation (which is referred to as «soft tabs»).
  • Consider breaking a line into several if it’s longer than 100 characters.

Saltos de línea y líneas en blanco

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();
}

Sin embargo, puedes optar por omitir los saltos de línea dentro de llaves:

  • Para operadores de miembros simples.
  • Para objetos simples, arrays o inicializadores de colección.
  • Para abstraciones de propiedades auto, indexaciones, o declaraciones de evento.
// 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};
        }
    }
}

Inserte una línea en blanco:

  • After a list of using statements.
  • Entre métodos, propiedades y declaraciones de tipo internas.
  • At the end of each file.

Las declaraciones de campos y constantes se pueden agrupar según la relevancia. En ese caso, considera insertar una línea en blanco entre los grupos para facilitar la lectura.

Evita insertar una línea en blanco:

  • Después de un corchete de apertura {.
  • Antes de un corchete de cierre ``}`.
  • After a comment block or a single-line comment.
  • Adyacente a otra línea en blanco.
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()
    {
    }
}

Usando espacios

Inserta un espacio:

  • Alrededor de un operador binario y ternario.
  • Between an opening parenthesis and if, for, foreach, catch, while, lock or using keywords.
  • Antes y dentro de un bloque de accesorios de una sola línea.
  • Entre los accesorios en un bloque de accesorios de una sola línea.
  • After a comma which is not at the end of a line.
  • After a semicolon in a for statement.
  • After a colon in a single line case statement.
  • En torno a los dos puntos en una declaración de tipo.
  • En torno a una flecha lambda.
  • After a single-line comment symbol (//), and before it if used at the end of a line.

No uses un espacio:

  • Después de un paréntesis en conversión de tipo.
  • inicialización de llaves de una sola línea.

El siguiente ejemplo muestra un uso adecuado de los espacios, de acuerdo con algunas de las convenciones antes mencionadas:

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.
    }
}

Convenciones para la definición de nombres

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.

Por último, considera elegir nombres descriptivos y no trates de acortarlos demasiado si eso afecta la legibilidad.

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

FindNearbyEnemy()?.Damage(weaponDamage);

Rather than:

FindNode()?.Change(wpnDmg);

Variables locales tipadas implícitamente

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;

Otras consideraciones

  • Usa modificadores de acceso explícito.
  • Use propiedades en lugar de campos no privados.
  • 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.
  • Considere usar operadores condicion nula(null-conditional) o inicializadores de tipo(type initializers) para hacer el código más compacto.
  • Usa el método seguro cuando exista la posibilidad de que el valor sea de otro tipo, y usa el método directo en caso contrario.