Compartilhar via


Convenções comuns de código C#

Convenções de código são essenciais para manter a legibilidade, a consistência e a colaboração de código em uma equipe de desenvolvimento. É mais fácil de entender, manter e estender o código que segue as práticas do setor e as diretrizes estabelecidas. A maioria dos projetos impõe um estilo consistente por meio de convenções de código. Os projetos dotnet/docs e dotnet/samples não são exceção. Nesta série de artigos, você aprenderá nossas convenções de codificação e as ferramentas que usamos para aplicá-las. Você pode tomar nossas convenções como estão ou modificá-las para atender às necessidades de sua equipe.

Escolhemos nossas convenções com base nas seguintes metas:

  1. Correção: nossos exemplos são copiados e colados em seus aplicativos. Esperamos isso, portanto, precisamos criar um código resiliente e correto, mesmo após várias edições.
  2. Ensino: a finalidade de nossos exemplos é ensinar tudo do .NET e C#. Por esse motivo, não colocamos restrições em nenhum recurso de idioma ou API. Em vez disso, esses exemplos ensinam quando um recurso é uma boa opção.
  3. Consistência: os leitores esperam uma experiência consistente em nosso conteúdo. Todos os exemplos devem estar em conformidade com o mesmo estilo.
  4. Adoção: atualizamos agressivamente nossos exemplos para usar novos recursos de linguagem. Essa prática conscientiza os novos recursos e os torna mais familiares para todos os desenvolvedores C#.

Importante

Essas diretrizes são usadas pela Microsoft para desenvolver exemplos e documentação. Elas foram adotados das diretrizes do .NET runtime, estilo de codificação C# e compilador C# (roslyn). Escolhemos essas diretrizes porque elas foram testadas ao longo de vários anos de desenvolvimento de software livre. Elas ajudaram os membros da comunidade a participar dos projetos de runtime e compilador. Elas devem ser um exemplo de convenções comuns em C# e não uma lista autoritativa (confira Diretrizes de design de estrutura para isso).

As metas de ensino e adoção são as razões pelas quais a convenção de codificação de documentos difere das convenções de runtime e compilador. O runtime e o compilador têm métricas de desempenho estritas para caminhos frequentes. Não se pode dizer o mesmo de muitos outros aplicativos. Nossa meta de ensino determina que não proibimos nenhuma construção. Em vez disso, os exemplos mostram quando os constructos devem ser usados. Atualizamos exemplos mais agressivamente do que a maioria dos aplicativos de produção. Nossa meta de adoção determina que mostramos o código que você deve escrever hoje, mesmo quando o código escrito no ano passado não precisa de alterações.

Este artigo explica nossas diretrizes. As diretrizes evoluíram ao longo do tempo e você encontrará exemplos que não seguem nossas diretrizes. Damos as boas-vindas a PRs que trazem esses exemplos para a conformidade, ou problemas que chamam nossa atenção para exemplos que devemos atualizar. Nossas diretrizes são de software livre e damos as boas-vindas a PRs e problemas. No entanto, se o envio alterar essas recomendações, abra um problema para discussão primeiro. Você é bem-vindo a usar nossas diretrizes ou adaptá-las às suas necessidades.

Ferramentas e analisadores

As ferramentas podem ajudar sua equipe a impor as convenções deles. Você pode habilitar a análise de código para impor as regras que preferir. Você também pode criar uma editorconfig para que o Visual Studio imponha automaticamente suas diretrizes de estilo. Como ponto de partida, você pode copiar o arquivo do repositório dotnet/docs para usar nosso estilo.

Essas ferramentas facilitam a adoção de suas diretrizes preferenciais para sua equipe. O Visual Studio aplica as regras em todos os arquivos .editorconfig no escopo para formatar seu código. Você pode usar várias configurações para impor convenções corporativas, convenções de equipe e até convenções de projeto granulares.

A análise de código produz avisos e diagnósticos quando as regras habilitadas são violadas. Você configura as regras que deseja aplicar ao seu projeto. Em seguida, cada build de CI notifica os desenvolvedores quando eles violam qualquer uma das regras.

IDs de diagnóstico

Diretrizes de linguagem

As seções a seguir descrevem as práticas que a equipe de documentos do .NET segue para preparar exemplos e exemplos de código. Em geral, siga estas práticas:

  • Utilize recursos de linguagem moderna e versões em C# sempre que possível.
  • Evite construções de linguagem obsoletas ou desatualizadas.
  • Somente capture exceções que possam ser tratadas corretamente; evite capturar exceções genéricas.
  • Use tipos de exceção específicos para fornecer mensagens de erro significativas.
  • Use consultas LINQ e métodos para manipulação de coleção para melhorar a legibilidade do código.
  • Use programação assíncrona com assíncrono e aguarde operações associadas a E/S.
  • Tenha cuidado com deadlocks e use Task.ConfigureAwait quando apropriado.
  • Use as palavras-chave de idioma para tipos de dados em vez dos tipos de runtime. Por exemplo, use string em vez de System.String, ou int em vez de System.Int32.
  • Use int em vez de tipos não assinados. O uso de int é comum em todo o C# e é mais fácil interagir com outras bibliotecas quando você usa int. As exceções são para a documentação específica para tipos de dados não assinados.
  • Use var somente quando um leitor puder inferir o tipo da expressão. Os leitores exibem nossos exemplos na plataforma de documentos. Eles não têm dicas de focalização ou ferramenta que exibem o tipo de variáveis.
  • Escreva código com clareza e simplicidade em mente.
  • Evite lógica de código excessivamente complexa e complicada.

Há diretrizes mais específicas a seguir.

Dados de cadeia de caracteres

  • Use a interpolação de cadeia de caracteres para concatenar cadeias de caracteres curtas, como é mostrado no código a seguir.

    string displayName = $"{nameList[n].LastName}, {nameList[n].FirstName}";
    
  • Para acrescentar cadeias de caracteres em loops, especialmente quando você estiver trabalhando com grandes quantidades de texto, use um objeto System.Text.StringBuilder.

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

Matrizes

  • Use a sintaxe concisa ao inicializar matrizes na linha da declaração. No exemplo a seguir, você não pode usar var em vez de string[].
string[] vowels1 = { "a", "e", "i", "o", "u" };
  • Se você usar uma instanciação explícita, poderá usar var.
var vowels2 = new string[] { "a", "e", "i", "o", "u" };

Delegados

  • Use Func<> e Action<>, em vez de definir tipos delegados. Em uma classe, defina o método delegado.
Action<string> actionExample1 = x => Console.WriteLine($"x is: {x}");

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

Func<string, int> funcExample1 = x => Convert.ToInt32(x);

Func<int, int, int> funcExample2 = (x, y) => x + y;
  • Chame o método usando a assinatura definida pelo delegado Func<> ou 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)}");
  • Se você criar instâncias de um tipo delegado, use a sintaxe concisa. Em uma classe, defina o tipo 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 a chame. A declaração a seguir mostra a sintaxe condensada.

    Del exampleDel2 = DelMethod;
    exampleDel2("Hey");
    
  • A declaração a seguir usa a sintaxe completa.

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

try-catchInstruções e using no tratamento de exceções

  • Use uma instrução try-catch para a maioria da manipulação de exceções.

    static double ComputeDistance(double x1, double y1, double x2, double y2)
    {
        try
        {
            return Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
        }
        catch (System.ArithmeticException ex)
        {
            Console.WriteLine($"Arithmetic overflow or underflow: {ex}");
            throw;
        }
    }
    
  • Simplifique o código usando a instrução using do #C. Se você tiver uma instrução try-finally na qual o único código do bloco finally é uma chamada para o método Dispose, use, em vez disso, uma instrução using.

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

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

    Você pode fazer a mesma coisa com uma instrução using.

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

    Use a nova sintaxe using que não requer chaves:

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

Operadores && e ||

  • Use && em vez de & e || em vez de | quando executar comparações, conforme mostrado no exemplo a seguir.

    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) is var result)
    {
        Console.WriteLine("Quotient: {0}", result);
    }
    else
    {
        Console.WriteLine("Attempted division by 0 ends up here.");
    }
    

Se o divisor for 0, a segunda cláusula na instrução if causará um erro em tempo de execução. Mas o operador && entra em curto-circuito quando a primeira expressão é falsa. Ou seja, ele não avalia a segunda expressão. O operador & avalia ambas, resultando em um erro em tempo de execução quando divisor é 0.

Operador new

  • Use uma das formas concisas de instanciação de objeto, conforme mostrado nas declarações a seguir.

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

    As declarações anteriores são equivalentes à declaração a seguir.

    ExampleClass secondExample = new ExampleClass();
    
  • Use inicializadores de objeto para simplificar a criação de objeto, conforme mostrado no exemplo a seguir.

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

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

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

Manipulação de eventos

  • Use uma expressão lambda para definir um manipulador de eventos que você não precisa remover mais tarde:
public Form2()
{
    this.Click += (s, e) =>
        {
            MessageBox.Show(
                ((MouseEventArgs)e).Location.ToString());
        };
}

A expressão lambda reduz a definição convencional a seguir.

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

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

Membros estáticos

Chame membros estáticos usando o nome de classe: ClassName.StaticMember. Essa prática torna o código mais legível, tornando o acesso estático limpo. Não qualifique um membro estático definido em uma classe base com o nome de uma classe derivada. Enquanto esse código é compilado, a leitura do código fica incorreta e o código poderá ser danificado no futuro se você adicionar um membro estático com o mesmo nome da classe derivada.

Consultas LINQ

  • Use nomes significativos para variáveis de consulta. O exemplo a seguir usa seattleCustomers para os clientes que estão localizados em Seattle.

    var seattleCustomers = from customer in customers
                           where customer.City == "Seattle"
                           select customer.Name;
    
  • Use aliases para se certificar de que os nomes de propriedades de tipos anônimos sejam colocados corretamente em maiúsculas, usando o padrão Pascal-Case.

    var localDistributors =
        from customer in customers
        join distributor in distributors on customer.City equals distributor.City
        select new { Customer = customer, Distributor = distributor };
    
  • Renomeie propriedades quando os nomes de propriedades no resultado forem ambíguos. Por exemplo, se a sua consulta retornar um nome de cliente e uma ID de distribuidor, em vez de deixá-los como Name e ID no resultado, renomeie-os para esclarecer que Name é o nome de um cliente, e ID é a identificação 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 };
    
  • Usa a digitação implícita na declaração de variáveis de consulta e de intervalo. Essa orientação sobre digitação implícita em consultas LINQ substitui as regras gerais para variáveis locais de tipo implícito. As consultas LINQ geralmente usam projeções que criam tipos anônimos. Outras expressões de consulta criam resultados com tipos genéricos aninhados. Variáveis de tipo implícito geralmente são mais legíveis.

    var seattleCustomers = from customer in customers
                           where customer.City == "Seattle"
                           select customer.Name;
    
  • Alinhe as cláusulas de consulta na cláusula from, conforme mostrado nos exemplos anteriores.

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

    var seattleCustomers2 = from customer in customers
                            where customer.City == "Seattle"
                            orderby customer.Name
                            select customer;
    
  • Use várias cláusulas from, em vez de uma cláusula join, para acessar as coleções internas. Por exemplo, cada coleção de objetos Student pode conter um conjunto de pontuações no teste. Quando a próxima consulta for executada, ela retorna cada pontuação que seja acima de 90, juntamente com o sobrenome do estudante 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 };
    

Variáveis locais de tipo implícito

  • Use a digitação implícita para variáveis locais quando o tipo da variável for óbvio do lado direito da atribuição.

    var message = "This is clearly a string.";
    var currentTemperature = 27;
    
  • Não use var quando o tipo não for aparente do lado direito da atribuição. Não suponha que o tipo esteja claro partir do nome de um método. Um tipo de variável é considerado claro se for um operador new, uma conversão explícita ou uma atribuição a um valor literal.

    int numberOfIterations = Convert.ToInt32(Console.ReadLine());
    int currentMaximum = ExampleClass.ResultSoFar();
    
  • Não use nomes de variáveis para especificar o tipo da variável. Ele pode não estar correto. Em vez disso, use o tipo para especificar o tipo e use o nome da variável para indicar as informações semânticas da variável. O exemplo a seguir deve usar string para o tipo e algo como iterations indicar o significado das informações lidas do console.

    var inputInt = Console.ReadLine();
    Console.WriteLine(inputInt);
    
  • Evite o uso de var em vez de dynamic. Use dynamic quando quiser uma inferência de tipo em tempo de execução. Para obter mais informações, confira Como usar o tipo dinâmico (Guia de Programação do C#).

  • Use a digitação implícita para a variável de loop em loops for.

    O exemplo a seguir usa digitação implícita em uma instrução for.

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

    O exemplo a seguir usa digitação explícita em uma instrução foreach.

    foreach (char ch in laugh)
    {
        if (ch == 'h')
        {
            Console.Write("H");
        }
        else
        {
            Console.Write(ch);
        }
    }
    Console.WriteLine();
    
  • use o tipo implícito para as sequências de resultados em consultas LINQ. A seção em LINQ explica que muitas consultas LINQ resultam em tipos anônimos em que tipos implícitos devem ser usados. Outras consultas resultam em tipos genéricos aninhados em var que é mais legível.

    Observação

    Tenha cuidado para não alterar acidentalmente um tipo de elemento da coleção iterável. Por exemplo, é fácil alternar de System.Linq.IQueryable para System.Collections.IEnumerable em uma instrução foreach, o que altera a execução de uma consulta.

Alguns de nossos exemplos explicam o tipo natural de uma expressão. Esses exemplos devem usar var para que o compilador escolha o tipo natural. Embora esses exemplos sejam menos óbvios, o uso de var é necessário para o exemplo. O texto deve explicar o comportamento.

Coloque as diretivas “using” fora da declaração de namespace

Quando uma diretiva using está fora de uma declaração de namespace, esse namespace importado é seu nome totalmente qualificado. O nome totalmente qualificado é mais claro. Quando a diretiva using está dentro do namespace, ela pode ser relativa a esse namespace ou ao seu nome totalmente qualificado.

using Azure;

namespace CoolStuff.AwesomeFeature
{
    public class Awesome
    {
        public void Stuff()
        {
            WaitUntil wait = WaitUntil.Completed;
            // ...
        }
    }
}

Supondo que haja uma referência (direta ou indireta) à classe WaitUntil.

Agora, vamos fazer uma pequena alteração:

namespace CoolStuff.AwesomeFeature
{
    using Azure;

    public class Awesome
    {
        public void Stuff()
        {
            WaitUntil wait = WaitUntil.Completed;
            // ...
        }
    }
}

E ela será compilada hoje. E amanhã. Mas, em algum momento da próxima semana, o código anterior (intocado) falha com dois erros:

- error CS0246: The type or namespace name 'WaitUntil' could not be found (are you missing a using directive or an assembly reference?)
- error CS0103: The name 'WaitUntil' does not exist in the current context

Uma das dependências introduziu esta classe em um namespace e termina com .Azure:

namespace CoolStuff.Azure
{
    public class SecretsManagement
    {
        public string FetchFromKeyVault(string vaultId, string secretId) { return null; }
    }
}

Uma diretiva using colocada dentro de um namespace diferencia contexto e complica a resolução do nome. Neste exemplo, é o primeiro namespace que ela encontra.

  • CoolStuff.AwesomeFeature.Azure
  • CoolStuff.Azure
  • Azure

Adicionar um novo namespace que corresponda a CoolStuff.Azure ou CoolStuff.AwesomeFeature.Azure seria combinado antes do namespace Azure global. Você poderia resolver isso adicionando o modificador global:: à declaração using. No entanto, é mais fácil colocar declarações using fora do namespace.

namespace CoolStuff.AwesomeFeature
{
    using global::Azure;

    public class Awesome
    {
        public void Stuff()
        {
            WaitUntil wait = WaitUntil.Completed;
            // ...
        }
    }
}

Diretrizes de estilo

Em geral, use o seguinte formato para exemplos de código:

  • Use quatro espaços para recuo. Não use guias.
  • Alinhe o código consistentemente para melhorar a legibilidade.
  • Limite linhas a 65 caracteres para aprimorar a legibilidade de código em documentos, especialmente em telas de dispositivos móveis.
  • Divida instruções longas em várias linhas para melhorar a clareza.
  • Use o estilo "Allman" para chaves: abra e feche sua própria nova linha. As chaves se alinham com o nível de recuo atual.
  • As quebras de linha devem ocorrer antes dos operadores binários, se necessário.

Estilo de comentário

  • Use comentários de linha única (//) para breves explicações.

  • Evite comentários de várias linhas (/* */) para explicações mais longas.
    Os comentários nos exemplos de código não são localizados. Isso significa que as explicações incorporadas no código não serão traduzidas. Um texto explicativo mais longo deve ser colocado no artigo complementar, para que possa ser localizado.

  • Para descrever métodos, classes, campos e todos os membros públicos, use comentários XML.

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

  • Comece o texto do comentário com uma letra maiúscula.

  • Termine o texto do comentário com um ponto final.

  • Insira um espaço entre o delimitador de comentário (//) e o texto do comentário, conforme mostrado no exemplo a seguir.

    // The following declaration creates a query. It does not run
    // the query.
    

Convenções de layout

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

  • Use as configurações padrão do Editor de códigos (recuo inteligente, recuos de quatro caracteres, guias salvas como espaços). Para obter mais informações, consulte Opções, Editor de Texto, C#, Formatação.

  • Gravar apenas uma instrução por linha.

  • Gravar apenas uma declaração por linha.

  • Se as linhas de continuação não forem recuadas automaticamente, recue-as uma parada de tabulação (quatro espaços).

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

  • Use parênteses para criar cláusulas em uma expressão aparente, conforme mostrado no código a seguir.

    if ((startX > endX) && (startX > previousX))
    {
        // Take appropriate action.
    }
    

As exceções são quando o exemplo explica o operador ou a precedência de expressão.

Segurança

Siga as diretrizes em Diretrizes de codificação segura.