C# Convenções de Codificação

As convenções de codificação servem os seguintes fins:

  • Criam um olhar consistente para o código, para que os leitores possam focar-se no conteúdo, não no layout.
  • Permitem que os leitores compreendam o código mais rapidamente, fazendo suposições com base em experiências anteriores.
  • Facilitam a cópia, a mudança e a manutenção do código.
  • Eles demonstram as melhores práticas.

Importante

As diretrizes deste artigo são utilizadas pela Microsoft para desenvolver amostras e documentação. Foram adotadas a partir das diretrizes .NET Runtime, C# Coding Style . Pode usá-las ou adaptá-las às suas necessidades. Os objetivos principais são a consistência e a legibilidade dentro do seu projeto, equipa, organização ou código fonte da empresa.

Convenções de nomenclatura

Existem várias convenções de nomeação a ter em conta ao escrever código C#.

Nos exemplos seguintes, qualquer uma das orientações relativas aos elementos marcados public também é aplicável quando se trabalha com protected e protected internal elementos, todos os quais se destinam a ser visíveis para chamadas externas.

Caso Pascal

Utilize o invólucro pascal ("PascalCasing") ao nomear um class, recordou struct.

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

Ao nomear um interface, use o invólucro pascal para além de pré-fixar o nome com um I. Isto indica claramente aos consumidores que é um interface.

public interface IWorkerQueue
{
}

Ao nomear public membros de tipos, tais como campos, propriedades, eventos, métodos e funções locais, utilize o invólucro 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;
        // ...
    }
}

Ao escrever registos posicionais, use o invólucro pascal para parâmetros, uma vez que são as propriedades públicas do registo.

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

Para obter mais informações sobre registos posicionais, consulte a sintaxe posicional para definição de propriedade.

Caso de camelo

Utilize o invólucro de camelo ("CamelCasing") ao nomear private ou internal campos e prefixá-los com _.

public class DataService
{
    private IWorkerQueue _workerQueue;
}

Dica

Ao editar o código C# que segue estas convenções de nomeação num IDE que suporta a conclusão da declaração, a dactilografia _ mostrará todos os membros com âmbito de objetos.

Ao trabalhar com static campos que são private ou internal, utilize o s_ prefixo e para utilização t_estática de rosca .

public class DataService
{
    private static IWorkerQueue s_workerQueue;

    [ThreadStatic]
    private static TimeSpan t_timeSpan;
}

Ao escrever parâmetros de método, utilize o invólucro de camelo.

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

Para obter mais informações sobre as convenções de nomeação C#, consulte C# Coding Style.

Convenções adicionais de nomeação

  • Exemplos que não incluem o uso de diretivas, use qualificações de espaço de nome. Se você sabe que um espaço de nome é importado por padrão em um projeto, você não precisa qualificar totalmente os nomes desse espaço de nome. Os nomes qualificados podem ser quebrados após um ponto (.) se forem demasiado longos para uma única linha, como mostra o exemplo seguinte.

    var currentPerformanceCounterCategory = new System.Diagnostics.
        PerformanceCounterCategory();
    
  • Não é preciso alterar os nomes dos objetos que foram criados usando as ferramentas de designer Visual Studio para que se encaixem noutras diretrizes.

Convenções de layout

Um bom layout usa formatação para enfatizar a estrutura do seu código e para facilitar a leitura do código. Exemplos e amostras da Microsoft estão em conformidade com as seguintes convenções:

  • Utilize as definições predefinidos do Code Editor (recuo inteligente, recuos de quatro caracteres, separadores guardados como espaços). Para mais informações, consulte Opções, Editor de Texto, C#, Formatting.

  • Escreva apenas uma declaração por linha.

  • Escreva apenas uma declaração por linha.

  • Se as linhas de continuação não forem recortadas automaticamente, inserem-nas uma paragem de separador (quatro espaços).

  • Adicione pelo menos uma linha em branco entre definições de método e definições de propriedade.

  • Use parênteses para tornar as cláusulas numa expressão aparente, como mostra o código seguinte.

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

Comentar convenções

  • Coloque o comentário numa linha separada, não no final de uma linha de código.

  • Comece a comentar texto com uma letra maiúscula.

  • Fim do texto de comentário com um período.

  • Insira um espaço entre o delimiter de comentários (//) e o texto de comentário, como mostra o exemplo seguinte.

    // The following declaration creates a query. It does not run
    // the query.
    
  • Não crie blocos formatados de asteriscos em torno de comentários.

  • Certifique-se de que todos os membros públicos têm os comentários XML necessários, fornecendo descrições apropriadas sobre o seu comportamento.

Diretrizes linguísticas

As secções seguintes descrevem práticas que a equipa C# segue para preparar exemplos de código e amostras.

Tipo de dados de corda

  • Utilize a interpolação de cordas para concatenar cordas curtas, como mostra o código seguinte.

    string displayName = $"{nameList[n].LastName}, {nameList[n].FirstName}";
    
  • Para apentar as cordas em loops, especialmente quando estiver a trabalhar com grandes quantidades de texto, use um StringBuilder objeto.

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

Variáveis locais escritas implicitamente

  • Use dactilografia implícita para variáveis locais quando o tipo da variável é óbvio do lado direito da atribuição, ou quando o tipo preciso não é importante.

    var var1 = "This is clearly a string.";
    var var2 = 27;
    
  • Não utilize var quando o tipo não estiver aparente do lado direito da atribuição. Não assuma que o tipo está claro a partir de um nome de método. Um tipo variável é considerado claro se é um new operador ou um elenco explícito.

    int var3 = Convert.ToInt32(Console.ReadLine()); 
    int var4 = ExampleClass.ResultSoFar();
    
  • Não confie no nome variável para especificar o tipo da variável. Pode não estar certo. No exemplo seguinte, o nome inputInt variável é enganador. É uma corda.

    var inputInt = Console.ReadLine();
    Console.WriteLine(inputInt);
    
  • Evite a utilização var em vez de dinâmica. Utilize dynamic quando quiser inferência do tipo de tempo de execução. Para mais informações, consulte utilizar a dinâmica do tipo (C# Guia de Programação).

  • Utilize a dactilografia implícita para determinar o tipo de variável do loop em for loops.

    O exemplo a seguir utiliza a dactilografia implícita num for comunicado.

    var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";
    var manyPhrases = new StringBuilder();
    for (var i = 0; i < 10000; i++)
    {
        manyPhrases.Append(phrase);
    }
    //Console.WriteLine("tra" + manyPhrases);
    
  • Não use dactilografia implícita para determinar o tipo de variável do loop em foreach loops. Na maioria dos casos, o tipo de elementos na coleção não é imediatamente óbvio. O nome da coleção não deve ser invocado apenas para inferir o tipo dos seus elementos.

    O exemplo a seguir utiliza a dactilografia explícita num foreach comunicado.

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

    Nota

    Tenha cuidado para não alterar acidentalmente um elemento da coleção iterável. Por exemplo, é fácil mudar de System.Linq.IQueryable um System.Collections.IEnumerableforeach comunicado, o que altera a execução de uma consulta.

Tipos de dados não assinados

Em geral, utilize int em vez de tipos não assinados. A utilização int é comum em todo o C#, e é mais fácil interagir com outras bibliotecas quando se utiliza int.

Matrizes

Utilize a sintaxe concisa quando rubricar os arrays na linha de declaração. No exemplo seguinte, note que não pode usar var em vez de string[].

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

Se utilizar uma instantânea explícita, pode utilizar var.

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

Se especificar um tamanho de matriz, tem de rubricar os elementos um de cada vez.

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

Delegados

Use Func<> e Action<> em vez de definir tipos de delegados. Numa aula, defina o método de 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;

Ligue para o método utilizando a assinatura definida pelo Func<> ou Action<> delegado.

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

Se criar instâncias de um tipo de delegado, utilize a sintaxe concisa. Numa aula, defina o tipo de delegado e um método que tenha uma assinatura correspondente.

public delegate void Del(string message);

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

Crie uma instância do tipo delegado e chame-o. A seguinte declaração mostra a sintaxe condensada.

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

A seguinte declaração utiliza a sintaxe completa.

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

try-catch e using declarações no tratamento de exceções

  • Utilize uma declaração de captura para a maioria das exceções.

    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 o seu código utilizando o C# utilizando a declaração. Se tiver uma declaração em que o único código do finally bloco é uma chamada para o Dispose método, utilize uma using declaração.

    No exemplo seguinte, a try-finally declaração apenas chama Dispose no finally bloco.

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

    Pode fazer o mesmo com uma using declaração.

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

    Nas versões C# 8 e posterior, utilize a nova using sintaxe que não requer aparelhos:

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

&& e || operadores

Para evitar exceções e aumentar o desempenho, ignorando comparações desnecessárias, utilize && em vez de & e || em vez de | quando realiza comparações, como mostra o exemplo seguinte.

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.");
}

Se o divisor for 0, a segunda cláusula da if declaração causaria um erro de tempo de execução. Mas o && operador faz curto-circuitos quando a primeira expressão é falsa. Isto é, não avalia a segunda expressão. O & operador avaliaria ambos, resultando num erro de tempo de execução quando divisor é 0.

new operador

  • Utilize uma das formas concisas de instantâneo do objeto, como mostram as seguintes declarações. O segundo exemplo mostra a sintaxe disponível a partir de C# 9.

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

    As declarações anteriores equivalem à seguinte declaração.

    ExampleClass instance2 = new ExampleClass();
    
  • Utilize os inicializadores de objetos para simplificar a criação de objetos, como mostra o exemplo a seguir.

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

    O exemplo a seguir define as mesmas propriedades que o exemplo anterior, mas não utiliza inicializadores.

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

Manipulação de eventos

Se estiver a definir um manipulador de eventos que não precisa de remover mais tarde, use uma expressão lambda.

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

A expressão lambda encurta a seguinte definição tradicional.

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

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

Membros estáticos

Ligue para membros estáticos usando o nome de classe: ClassName.StaticMember. Esta prática torna o código mais legível, tornando o acesso estático claro. Não qualifique um membro estático definido numa classe base com o nome de uma classe derivada. Enquanto esse código compila, a legibilidade do código é enganosa, e o código pode quebrar no futuro se adicionar um membro estático com o mesmo nome à classe derivada.

Consultas linq

  • Use nomes significativos para variáveis de consulta. O exemplo a seguir utiliza-se seattleCustomers para clientes localizados em Seattle.

    var seattleCustomers = from customer in customers
                           where customer.City == "Seattle"
                           select customer.Name;
    
  • Use pseudónimos para se certificar de que os nomes de propriedades de tipos anónimos são corretamente maiúsculas, usando o invólucro Pascal.

    var localDistributors =
        from customer in customers
        join distributor in distributors on customer.City equals distributor.City
        select new { Customer = customer, Distributor = distributor };
    
  • Mudar de nome de propriedades quando os nomes da propriedade no resultado seriam ambíguos. Por exemplo, se a sua consulta devolver um nome de cliente e um ID do distribuidor, em vez de os deixar como Name e ID no resultado, mude-os para esclarecer que Name é o nome de um cliente, e ID é o ID de um 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 };
    
  • Utilize dactilografia implícita na declaração de variáveis de consulta e variáveis de alcance.

    var seattleCustomers = from customer in customers
                           where customer.City == "Seattle"
                           select customer.Name;
    
  • Alinhe as cláusulas de consulta nos termos da from cláusula, como mostram os exemplos anteriores.

  • Utilize where cláusulas antes de outras cláusulas de consulta para garantir que as cláusulas de consulta posteriores operem no conjunto de dados reduzido e filtrado.

    var seattleCustomers2 = from customer in customers
                            where customer.City == "Seattle"
                            orderby customer.Name
                            select customer;
    
  • Use várias from cláusulas em vez de uma join cláusula para aceder a coleções internas. Por exemplo, uma coleção de Student objetos pode conter uma coleção de pontuações de teste. Quando a seguinte consulta é executada, devolve cada partitura que tenha mais de 90, juntamente com o último nome do aluno que recebeu a pontuação.

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

Segurança

Siga as diretrizes em Diretrizes de Codificação Segura.

Ver também