Convenciones de código de C#

Las convenciones de codificación tienen los objetivos siguientes:

  • Crean una apariencia coherente en el código, para que los lectores puedan centrarse en el contenido, no en el diseño.
  • Permiten a los lectores comprender el código más rápidamente al hacer suposiciones basadas en la experiencia anterior.
  • Facilitan la copia, el cambio y el mantenimiento del código.
  • Muestran los procedimientos recomendados de C#.

Importante

Microsoft usa las instrucciones de este artículo para desarrollar ejemplos y documentación. Se adoptaron a partir del runtime de .NET, directrices de estilo de codificación de C #. Puede usarlas o adaptarlas a sus necesidades. Los objetivos principales son la coherencia y la legibilidad dentro del proyecto, equipo, organización o código fuente de la empresa.

Convenciones de nomenclatura

Hay varias convenciones de nomenclatura que se deben tener en cuenta al escribir código de C#.

En los ejemplos siguientes, cualquiera publicprotectedprotected internal de las instrucciones relativas a los elementos marcados también es aplicable al trabajar con elementos y , todos los cuales están diseñados para ser visibles para los llamadores externos.

Pascal case

Use mayúsculas y minúsculas pascal ("PascalCasing") al asignar un nombre a class, recordo struct.

public class DataService
{
}
public record PhysicalAddress(
    string Street,
    string City,
    string StateOrProvince,
    string ZipCode);
public struct ValueCoordinate
{
}

Al asignar un nombre a interface, use la grafía Pascal además de agregar el prefijo I al nombre. Esto indica claramente a los consumidores que es un elemento interface.

public interface IWorkerQueue
{
}

Al asignar nombres a public miembros de tipos, como campos, propiedades, eventos, métodos y funciones locales, use mayúsculas y minúsculas pascal.

public class ExampleEvents
{
    // A public field, these should be used sparingly
    public bool IsValid;

    // An init-only property
    public IWorkerQueue WorkerQueue { get; init; }

    // An event
    public event Action EventProcessing;

    // Method
    public void StartEventProcessing()
    {
        // Local function
        static int CountQueueItems() => WorkerQueue.Count;
        // ...
    }
}

Al escribir registros posicionales, use mayúsculas y minúsculas pascal para los parámetros, ya que son las propiedades públicas del registro.

public record PhysicalAddress(
    string Street,
    string City,
    string StateOrProvince,
    string ZipCode);

Para obtener más información sobre los registros posicionales, vea Sintaxis posicional para la definición de propiedades.

Grafía Camel

Use mayúsculas y minúsculas camel ("camelCasing") al asignar nombres a private campos internal o y antefirrlos con _.

public class DataService
{
    private IWorkerQueue _workerQueue;
}

Sugerencia

Al editar código de C# que sigue estas convenciones de nomenclatura en un IDE que admite la finalización de instrucciones, al escribir _ se mostrarán todos los miembros con ámbito de objeto.

Al trabajar con campos static que sean private o internal, use el prefijo s_ y, para el subproceso estático, use t_.

public class DataService
{
    private static IWorkerQueue s_workerQueue;

    [ThreadStatic]
    private static TimeSpan t_timeSpan;
}

Al escribir parámetros de método, use la grafía Camel.

public T SomeMethod<T>(int someNumber, bool isValid)
{
}

Para obtener más información sobre las convenciones de nomenclatura de C#, vea Estilo de codificación de C#.

Convenciones de nomenclatura adicionales

  • En ejemplos que no incluyan directivas using, use calificaciones de espacio de nombres. Si sabe que un espacio de nombres se importa en un proyecto de forma predeterminada, no es necesario completar los nombres de ese espacio de nombres. Los nombres completos pueden partirse después de un punto (.) si son demasiado largos para una sola línea, como se muestra en el ejemplo siguiente.

    var currentPerformanceCounterCategory = new System.Diagnostics.
        PerformanceCounterCategory();
    
  • No es necesario cambiar los nombres de objetos que se crearon con las herramientas del diseñador de Visual Studio para que se ajusten a otras directrices.

Convenciones de diseño

Un buen diseño utiliza un formato que destaque la estructura del código y haga que el código sea más fácil de leer. Las muestras y ejemplos de Microsoft cumplen las convenciones siguientes:

  • Utilice la configuración del Editor de código predeterminada (sangría automática, sangrías de 4 caracteres, tabulaciones guardadas como espacios). Para obtener más información, vea Opciones, editor de texto, C#, formato.

  • Escriba solo una instrucción por línea.

  • Escriba solo una declaración por línea.

  • Si a las líneas de continuación no se les aplica sangría automáticamente, hágalo con una tabulación (cuatro espacios).

  • Agregue al menos una línea en blanco entre las definiciones de método y las de propiedad.

  • Utilice paréntesis para que las cláusulas de una expresión sean evidentes, como se muestra en el código siguiente.

    if ((val1 > val2) && (val1 > val3))
    {
        // Take appropriate action.
    }
    

Convenciones de los comentarios

  • Coloque el comentario en una línea independiente, no al final de una línea de código.

  • Comience el texto del comentario con una letra mayúscula.

  • Finalice el texto del comentario con un punto.

  • Inserte un espacio entre el delimitador de comentario (//) y el texto del comentario, como se muestra en el ejemplo siguiente.

    // The following declaration creates a query. It does not run
    // the query.
    
  • No crees bloques de formato con asteriscos alrededor de los comentarios.

  • Asegúrese de que todos los miembros públicos tienen los comentarios XML necesarios que proporcionan descripciones adecuadas sobre su comportamiento.

Convenciones de lenguaje

En las secciones siguientes se describen las prácticas que sigue el equipo C# para preparar las muestras y ejemplos de código.

Tipo de datos String

  • Use interpolación de cadenas para concatenar cadenas cortas, como se muestra en el código siguiente.

    string displayName = $"{nameList[n].LastName}, {nameList[n].FirstName}";
    
  • Para anexar cadenas en bucles, especialmente cuando se trabaja con grandes cantidades de texto, utilice un objeto StringBuilder.

    var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";
    var manyPhrases = new StringBuilder();
    for (var i = 0; i < 10000; i++)
    {
        manyPhrases.Append(phrase);
    }
    //Console.WriteLine("tra" + manyPhrases);
    

Variables locales con tipo implícito

  • Use tipos implícitos para las variables locales cuando el tipo de la variable sea obvio desde el lado derecho de la asignación, o cuando el tipo exacto no sea importante.

    var var1 = "This is clearly a string.";
    var var2 = 27;
    
  • No use var cuando el tipo no sea evidente desde el lado derecho de la asignación. No asuma que el tipo está claro a partir de un nombre de método. Se considera que un tipo de variable es claro si es un operador new o una conversión explícita.

    int var3 = Convert.ToInt32(Console.ReadLine()); 
    int var4 = ExampleClass.ResultSoFar();
    
  • No confíe en el nombre de variable para especificar el tipo de la variable. Puede no ser correcto. En el siguiente ejemplo, el nombre de la variable inputInt es engañoso. Es una cadena.

    var inputInt = Console.ReadLine();
    Console.WriteLine(inputInt);
    
  • Evite el uso de var en lugar de dynamic. Use dynamic cuando desee la inferencia de tipos en tiempo de ejecución. Para obtener más información, vea Uso de tipo dinámico (Guía de programación de C#).

  • Use la escritura implícita para determinar el tipo de la variable de bucle en for bucles.

    En el ejemplo siguiente se usan tipos implícitos en una instrucción for.

    var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";
    var manyPhrases = new StringBuilder();
    for (var i = 0; i < 10000; i++)
    {
        manyPhrases.Append(phrase);
    }
    //Console.WriteLine("tra" + manyPhrases);
    
  • No use la escritura implícita para determinar el tipo de la variable de bucle en foreach bucles.

    En el ejemplo siguiente se usan tipos explícitos en una instrucción foreach.

    foreach (char ch in laugh)
    {
        if (ch == 'h')
            Console.Write("H");
        else
            Console.Write(ch);
    }
    Console.WriteLine();
    

    Nota

    Tenga cuidado de no cambiar accidentalmente un tipo de elemento de la colección iterable. Por ejemplo, es fácil cambiar de System.Linq.IQueryable a System.Collections.IEnumerable en una instrucción foreach, lo cual cambia la ejecución de una consulta.

Tipos de datos sin signo

En general, utilice int en lugar de tipos sin signo. El uso de int es común en todo C#, y es más fácil interactuar con otras bibliotecas cuando se usa int.

Matrices

Utilice sintaxis concisa para inicializar las matrices en la línea de declaración. En el siguiente ejemplo, observe que no puede utilizar var en lugar de string[].

string[] vowels1 = { "a", "e", "i", "o", "u" };

Si usa la creación de instancias explícita, puede usar var.

var vowels2 = new string[] { "a", "e", "i", "o", "u" };

Si especifica un tamaño de matriz, tiene que inicializar los elementos de uno en uno.

var vowels3 = new string[5];
vowels3[0] = "a";
vowels3[1] = "e";
// And so on.

Delegados

Use Func<> y Action<> en lugar de definir tipos de delegado. En una clase, defina el método delegado.

public static Action<string> ActionExample1 = x => Console.WriteLine($"x is: {x}");

public static Action<string, string> ActionExample2 = (x, y) => 
    Console.WriteLine($"x is: {x}, y is {y}");

public static Func<string, int> FuncExample1 = x => Convert.ToInt32(x);

public static Func<int, int, int> FuncExample2 = (x, y) => x + y;

Llame al método con la signatura definida por el delegado Func<> o Action<>.

ActionExample1("string for x");

ActionExample2("string for x", "string for y");

Console.WriteLine($"The value is {FuncExample1("1")}");

Console.WriteLine($"The sum is {FuncExample2(1, 2)}");

Si crea instancias de un tipo de delegado, utilice la sintaxis concisa. En una clase, defina el tipo de delegado y un método que tenga una firma coincidente.

public delegate void Del(string message);

public static void DelMethod(string str)
{
    Console.WriteLine("DelMethod argument: {0}", str);
}

Cree una instancia del tipo de delegado y llámela. La siguiente declaración muestra la sintaxis condensada.

Del exampleDel2 = DelMethod;
exampleDel2("Hey");

La siguiente declaración utiliza la sintaxis completa.

Del exampleDel1 = new Del(DelMethod);
exampleDel1("Hey");

Instrucciones try-catch y using en el control de excepciones

  • Use una instrucción try-catch en la mayoría de casos de control de excepciones.

    static string GetValueFromArray(string[] array, int index)
    {
        try
        {
            return array[index];
        }
        catch (System.IndexOutOfRangeException ex)
        {
            Console.WriteLine("Index is out of range: {0}", index);
            throw;
        }
    }
    
  • Simplifique el código mediante la instrucción using de C#. Si tiene una instrucción try-finally en la que el único código del bloque finally es una llamada al método Dispose, use en su lugar una instrucción using.

    En el ejemplo siguiente, la instrucción try-finally solo llama a Dispose en el bloque finally.

    Font font1 = new Font("Arial", 10.0f);
    try
    {
        byte charset = font1.GdiCharSet;
    }
    finally
    {
        if (font1 != null)
        {
            ((IDisposable)font1).Dispose();
        }
    }
    

    Puede hacer lo mismo con una instrucción using.

    using (Font font2 = new Font("Arial", 10.0f))
    {
        byte charset2 = font2.GdiCharSet;
    }
    

    En C# 8 y versiones posteriores, use la nueva sintaxis using que no requiere llaves:

    using Font font3 = new Font("Arial", 10.0f);
    byte charset3 = font3.GdiCharSet;
    

Operadores && y ||

Para evitar excepciones y aumentar el rendimiento omitiendo las comparaciones innecesarias, use && en lugar de & y || en lugar de | cuando realice comparaciones, como se muestra en el ejemplo siguiente.

Console.Write("Enter a dividend: ");
int dividend = Convert.ToInt32(Console.ReadLine());

Console.Write("Enter a divisor: ");
int divisor = Convert.ToInt32(Console.ReadLine());

if ((divisor != 0) && (dividend / divisor > 0))
{
    Console.WriteLine("Quotient: {0}", dividend / divisor);
}
else
{
    Console.WriteLine("Attempted division by 0 ends up here.");
}

Si el divisor es 0, la segunda cláusula de la instrucción if produciría un error en tiempo de ejecución. Pero el && operador cortocircuidores cuando la primera expresión es false. Es decir, no evalúa la segunda expresión. El & operador evaluaría ambos, lo que daría lugar a un error en tiempo de ejecución cuando divisor es 0.

Operador new

  • Use una de las formas concisas de creación de instancias de objeto, tal como se muestra en las declaraciones siguientes. En el segundo ejemplo se muestra la sintaxis que está disponible a partir de C# 9.

    var instance1 = new ExampleClass();
    
    ExampleClass instance2 = new();
    

    Las declaraciones anteriores son equivalentes a la siguiente declaración.

    ExampleClass instance2 = new ExampleClass();
    
  • Use inicializadores de objeto para simplificar la creación de objetos, tal y como se muestra en el ejemplo siguiente.

    var instance3 = new ExampleClass { Name = "Desktop", ID = 37414,
        Location = "Redmond", Age = 2.3 };
    

    En el ejemplo siguiente se establecen las mismas propiedades que en el ejemplo anterior, pero no se utilizan inicializadores.

    var instance4 = new ExampleClass();
    instance4.Name = "Desktop";
    instance4.ID = 37414;
    instance4.Location = "Redmond";
    instance4.Age = 2.3;
    

Control de eventos

Si va a definir un controlador de eventos que no es necesario quitar más tarde, utilice una expresión lambda.

public Form2()
{
    this.Click += (s, e) =>
        {
            MessageBox.Show(
                ((MouseEventArgs)e).Location.ToString());
        };
}

La expresión lambda acorta la siguiente definición tradicional.

public Form1()
{
    this.Click += new EventHandler(Form1_Click);
}

void Form1_Click(object? sender, EventArgs e)
{
    MessageBox.Show(((MouseEventArgs)e).Location.ToString());
}

Miembros estáticos

Llame a miembros estáticos con el nombre de clase: ClassName.StaticMember. Esta práctica hace que el código sea más legible al clarificar el acceso estático. No califique un miembro estático definido en una clase base con el nombre de una clase derivada. Mientras el código se compila, su legibilidad se presta a confusión, y puede interrumpirse en el futuro si se agrega a un miembro estático con el mismo nombre a la clase derivada.

Consultas LINQ

  • Utilice nombres descriptivos para las variables de consulta. En el ejemplo siguiente, se utiliza seattleCustomers para los clientes que se encuentran en Seattle.

    var seattleCustomers = from customer in customers
                           where customer.City == "Seattle"
                           select customer.Name;
    
  • Utilice alias para asegurarse de que los nombres de propiedad de tipos anónimos se escriben correctamente con mayúscula o minúscula, usando para ello la grafía Pascal.

    var localDistributors =
        from customer in customers
        join distributor in distributors on customer.City equals distributor.City
        select new { Customer = customer, Distributor = distributor };
    
  • Cambie el nombre de las propiedades cuando puedan ser ambiguos en el resultado. Por ejemplo, si la consulta devuelve un nombre de cliente y un identificador de distribuidor, en lugar de dejarlos como Name e ID en el resultado, cambie su nombre para aclarar que Name es el nombre de un cliente e ID es el identificador de un distribuidor.

    var localDistributors2 =
        from customer in customers
        join distributor in distributors on customer.City equals distributor.City
        select new { CustomerName = customer.Name, DistributorID = distributor.ID };
    
  • Utilice tipos implícitos en la declaración de variables de consulta y variables de intervalo.

    var seattleCustomers = from customer in customers
                           where customer.City == "Seattle"
                           select customer.Name;
    
  • Alinee las cláusulas de consulta en la from cláusula , como se muestra en los ejemplos anteriores.

  • Use where cláusulas antes que otras cláusulas de consulta para asegurarse de que las cláusulas de consulta posteriores funcionan en el conjunto reducido y filtrado de datos.

    var seattleCustomers2 = from customer in customers
                            where customer.City == "Seattle"
                            orderby customer.Name
                            select customer;
    
  • Use varias from cláusulas en lugar de una cláusula para acceder a join colecciones internas. Por ejemplo, una colección de objetos Student podría contener cada uno un conjunto de resultados de exámenes. Cuando se ejecuta la siguiente consulta, devuelve cada resultado superior a 90, además del apellido del alumno que recibió la puntuación.

    var scoreQuery = from student in students
                     from score in student.Scores!
                     where score > 90
                     select new { Last = student.LastName, score };
    

Seguridad

Siga las instrucciones de Instrucciones de codificación segura.

Consulte también