Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Embora o compilador não tenha um pré-processador separado, ele processa as diretivas descritas nesta seção como se houvesse uma. Use essas diretivas para ajudar na compilação condicional. Ao contrário das diretivas de C e C++, não é possível usar essas diretivas para criar macros. Uma diretiva de pré-processador deve ser a única instrução em uma linha.
A linguagem C# faz referência a documentos da versão mais recentemente lançada da linguagem C#. Ele também contém a documentação inicial para recursos em visualizações públicas para a próxima versão do idioma.
A documentação identifica qualquer recurso introduzido pela primeira vez nas três últimas versões do idioma ou nas versões prévias públicas atuais.
Dica
Para descobrir quando um recurso foi introduzido pela primeira vez em C#, consulte o artigo sobre o histórico de versão da linguagem C#.
Aplicativos baseados em arquivo
Aplicativos baseados em arquivo são programas que você compila e executa usando dotnet run Program.cs (ou qualquer *.cs arquivo). O compilador C# ignora essas diretivas de pré-processador, mas o sistema de build as analisa para produzir a saída. Essas diretivas geram avisos quando encontradas em uma compilação baseada em projeto.
O compilador C# ignora qualquer diretiva de pré-processador que comece com #: ou #!.
A #! diretiva de pré-processador permite que shells unix executem diretamente um arquivo C# usando dotnet run. Por exemplo:
#!/usr/bin/env dotnet run
Console.WriteLine("Hello");
O snippet de código anterior informa um shell unix para executar o arquivo usando dotnet run. O /usr/bin/env comando localiza o dotnet executável em seu PATH, tornando essa abordagem portátil em diferentes distribuições unix e macOS. A #! linha deve ser a primeira linha no arquivo e os tokens a seguir são o programa a ser executado. Você precisa habilitar a permissão executar (x) no arquivo C# para esse recurso.
As #: diretivas usadas em aplicativos baseados em arquivo são descritas na referência de aplicativos baseados em arquivo.
Outras ferramentas podem adicionar novos tokens seguindo a #: convenção.
Contexto que permite valor nulo
A diretiva de pré-processador #nullable define as anotações e os sinalizadores de aviso no contexto anulável. Essa diretiva controla se as anotações anuláveis têm efeito e se os avisos de nulidade são dados. Cada sinalizador está desabilitado ou habilitado.
Você pode especificar ambos os contextos no nível do projeto (fora do código-fonte C#) adicionando o Nullable elemento ao PropertyGroup elemento. A diretiva #nullable controla a anotação e os sinalizadores de aviso e tem precedência sobre as configurações no nível do projeto. Uma diretiva define o sinalizador que controla até que outra diretiva o substitua ou até o final do arquivo de origem.
O efeito das diretivas é o seguinte:
-
#nullable disable: define o contexto anulável desabilitado. -
#nullable enable: define o contexto anulável habilitado. -
#nullable restore: restaura o contexto anulável para as configurações do projeto. -
#nullable disable annotations: configura o sinalizador de anotações no contexto anulável para desabilitado. -
#nullable enable annotations: configura o sinalizador de anotações no contexto anulável para habilitado. -
#nullable restore annotations: restaura o indicador de anotações no contexto anulável para as configurações do projeto. -
#nullable disable warnings: define o sinalizador de aviso no contexto anulável para desabilitado. -
#nullable enable warnings: define o sinalizador de aviso no contexto anulável para habilitado. -
#nullable restore warnings: restaura o sinalizador de aviso no contexto anulável para as configurações do projeto.
Compilação condicional
Use quatro diretivas de pré-processador para controlar a compilação condicional:
-
#if: inicia uma compilação condicional. O compilador compila o código somente se o símbolo especificado for definido. -
#elif: fecha a compilação condicional anterior e abre uma nova compilação condicional com base no caso do símbolo especificado ser definido. -
#else: fecha a compilação condicional anterior e abre uma nova compilação condicional se o símbolo especificado anteriormente não estiver definido. -
#endif: fecha a compilação condicional anterior.
O sistema de compilação também está ciente dos símbolos de pré-processamento predefinidos que representam várias estruturas de destino em projetos no estilo SDK. Eles são úteis ao criar aplicativos que podem ter como destino mais de uma versão do .NET.
| Frameworks de destino | Símbolos | Símbolos adicionais (disponível em SDKs do .NET 5+) |
Símbolos de plataforma (disponíveis somente quando você especifica um TFM específico do sistema operacional) |
|---|---|---|---|
| .NET Framework |
NETFRAMEWORK, NET481, NET48, NET472, , NET471, NET47, , NET462, NET461, NET46, NET452, , NET451, NET45, NET40, , NET35NET20 |
NET48_OR_GREATER, NET472_OR_GREATER, NET471_OR_GREATER, NET47_OR_GREATER, , NET462_OR_GREATER, NET461_OR_GREATER, , NET46_OR_GREATER, NET452_OR_GREATER, NET451_OR_GREATER, NET45_OR_GREATER, , NET40_OR_GREATER, , NET35_OR_GREATERNET20_OR_GREATER |
|
| .NET Standard |
NETSTANDARD, NETSTANDARD2_1, NETSTANDARD2_0, NETSTANDARD1_6, NETSTANDARD1_5, , NETSTANDARD1_4, NETSTANDARD1_3, , NETSTANDARD1_2, NETSTANDARD1_1, NETSTANDARD1_0 |
NETSTANDARD2_1_OR_GREATER, NETSTANDARD2_0_OR_GREATER, NETSTANDARD1_6_OR_GREATER, NETSTANDARD1_5_OR_GREATER, , NETSTANDARD1_4_OR_GREATER, NETSTANDARD1_3_OR_GREATER, NETSTANDARD1_2_OR_GREATER, , NETSTANDARD1_1_OR_GREATERNETSTANDARD1_0_OR_GREATER |
|
| .NET 5+ (e .NET Core) |
NET, NET10_0, NET9_0, NET8_0, , NET7_0, NET6_0, , NET5_0, NETCOREAPP, NETCOREAPP3_1, NETCOREAPP3_0, , NETCOREAPP2_2, NETCOREAPP2_1, NETCOREAPP2_0, , NETCOREAPP1_1NETCOREAPP1_0 |
NET10_0_OR_GREATER, NET9_0_OR_GREATER, NET8_0_OR_GREATER, NET7_0_OR_GREATER, , NET6_0_OR_GREATER, NET5_0_OR_GREATER, , NETCOREAPP3_1_OR_GREATER, NETCOREAPP3_0_OR_GREATER, NETCOREAPP2_2_OR_GREATER, NETCOREAPP2_1_OR_GREATER, , NETCOREAPP2_0_OR_GREATER, , NETCOREAPP1_1_OR_GREATERNETCOREAPP1_0_OR_GREATER |
ANDROID, BROWSER, IOS, MACCATALYST, , MACOS, TVOS, , WINDOWS[OS][version] (por exemplo, IOS15_1),[OS][version]_OR_GREATER (por exemplo, IOS15_1_OR_GREATER) |
Observação
- Os símbolos sem versão são definidos independentemente da versão para a qual você está direcionando.
- Os símbolos específicos à versão são definidos apenas para a versão que você está direcionando.
- Os símbolos
<framework>_OR_GREATERsão definidos para a versão que você está direcionando e todas as versões anteriores. Por exemplo, se você estiver direcionando .NET Framework 2.0, os seguintes símbolos serão definidos:NET20,NET20_OR_GREATER,NET11_OR_GREATEReNET10_OR_GREATER. - Os símbolos
NETSTANDARD<x>_<y>_OR_GREATERsão definidos apenas para destinos .NET Standard, e não para destinos que implementam o .NET Standard, como o .NET Core e o .NET Framework. - Eles são diferentes dos TFMs (Moniker da Estrutura de Destino) usados pela propriedade MSBuild
TargetFrameworke pelo NuGet.
Observação
Para projetos tradicionais, não estilo SDK, você precisa configurar manualmente os símbolos de compilação condicional para as diferentes estruturas de destino no Visual Studio por meio das páginas de propriedades do projeto.
Outros símbolos predefinidos incluem as constantes DEBUG e TRACE. Use #define para substituir os valores definidos para o projeto. O DEBUG símbolo, por exemplo, é definido automaticamente dependendo das propriedades de configuração de build (modo "Depurar" ou "Versão").
O compilador C# compila o código entre a diretiva #if e a diretiva #endif somente se o símbolo especificado está definido ou não definido quando o operador ! não é usado. Ao contrário de C e C++, você não pode atribuir um valor numérico a um símbolo. A instrução #if em C# é booliana e só testa se o símbolo está definido ou não. Por exemplo, o seguinte código é compilado quando DEBUG é definido:
#if DEBUG
Console.WriteLine("Debug version");
#endif
O seguinte código é compilado quando MYTESTnão é definido:
#if !MYTEST
Console.WriteLine("MYTEST is not defined");
#endif
Use os operadores == (igualdade) e != (desigualdade) para testar os bool valores true ou false.
true significa que o símbolo foi definido. A instrução #if DEBUG tem o mesmo significado que #if (DEBUG == true). Use os && operadores (e), || (ou)e ! (não) para avaliar se vários símbolos são definidos. Também é possível agrupar os símbolos e operadores com parênteses.
O exemplo a seguir mostra uma diretiva complexa que permite que seu código aproveite os recursos mais recentes do .NET, mantendo-se compatível com versões anteriores. Por exemplo, imagine que você esteja usando um pacote NuGet em seu código, mas o pacote dá suporte apenas para o .NET 6 e superior, bem como com o .NET Standard 2.0 e superior:
#if (NET6_0_OR_GREATER || NETSTANDARD2_0_OR_GREATER)
Console.WriteLine("Using .NET 6+ or .NET Standard 2+ code.");
#else
Console.WriteLine("Using older code that doesn't support the above .NET versions.");
#endif
#if, juntamente com as diretivas #else, #elif, #endif, #define e #undef permite incluir ou excluir código com base na existência de um ou mais símbolos. A compilação condicional pode ser útil ao compilar código para uma compilação de depuração ou ao compilar para uma configuração específica.
O #elif permite criar uma diretiva condicional composta. O compilador avaliará a #elif expressão se as expressões de diretiva anteriores #if nem opcionais, opcionais, #elif forem avaliadas como true. Se uma expressão #elif for avaliada como true, o compilador avaliará todo o código entre #elif e a próxima diretiva condicional. Por exemplo:
#define VC7
//...
#if DEBUG
Console.WriteLine("Debug build");
#elif VC7
Console.WriteLine("Visual Studio 7");
#endif
O #else permite criar uma diretiva condicional composta. Se nenhuma das expressões nas diretivas anteriores #if ou (opcionais) #elif for avaliada true, o compilador avaliará todo o código entre #else e o próximo #endif.
#endif deve ser a próxima diretiva de pré-processador após #else.
#endif especifica o final de uma diretiva condicional, que começou com a diretiva #if.
O exemplo a seguir mostra como definir um MYTEST símbolo em um arquivo e, em seguida, testar os valores dos símbolos e DEBUG dos MYTEST símbolos. A saída deste exemplo depende se você criou o projeto no modo de configuração de Depuração ou Versão .
#define MYTEST
using System;
public class MyClass
{
static void Main()
{
#if (DEBUG && !MYTEST)
Console.WriteLine("DEBUG is defined");
#elif (!DEBUG && MYTEST)
Console.WriteLine("MYTEST is defined");
#elif (DEBUG && MYTEST)
Console.WriteLine("DEBUG and MYTEST are defined");
#else
Console.WriteLine("DEBUG and MYTEST are not defined");
#endif
}
}
O exemplo a seguir mostra como testar estruturas de destino diferentes para que você possa usar APIs mais recentes quando possível:
public class MyClass
{
static void Main()
{
#if NET40
WebClient _client = new WebClient();
#else
HttpClient _client = new HttpClient();
#endif
}
//...
}
Definindo símbolos
Use as duas diretivas de pré-processador a seguir para definir ou indefinir símbolos para compilação condicional:
-
#define: define um símbolo. -
#undef: anula a definição de um símbolo.
Use #define para definir um símbolo. Quando você usa o símbolo como a expressão passada para a diretiva #if, a expressão é avaliada como true, como mostra o exemplo a seguir:
#define VERBOSE
#if VERBOSE
Console.WriteLine("Verbose output version");
#endif
Observação
Em C#, as constantes primitivas devem ser definidas usando a const palavra-chave. Uma declaração const cria um membro static que não pode ser modificado em runtime. A diretiva #define não pode ser usada para declarar valores de constantes como normalmente é feito no C e C++. Se você tiver várias dessas constantes, considere criar uma classe "Constantes" separada para guardá-las.
Use símbolos para especificar condições para compilação. Teste o símbolo usando ou #if#elif. Você também pode usar o ConditionalAttribute para executar uma compilação condicional. É possível definir um símbolo, mas não é possível atribuir um valor a um símbolo. A diretiva #define deve ser exibida no arquivo antes de usar as instruções que também não são diretivas de pré-processador. Você também pode definir um símbolo usando a opção do compilador DefineConstants . Indefinir um símbolo usando #undef.
Definindo regiões
Defina regiões de código que você pode recolher em uma estrutura de tópicos usando as duas diretivas de pré-processador a seguir:
-
#region: inicia uma região. -
#endregion: encerra uma região.
#region permite que você especifique um bloco de código que pode ser expandido ou recolhido ao usar o recurso de estrutura de tópicos do editor de código. Em arquivos de código mais longos, é conveniente recolher ou ocultar uma ou mais regiões para que você possa se concentrar na parte do arquivo que está trabalhando no momento. O exemplo a seguir mostra como definir uma região:
#region MyClass definition
public class MyClass
{
static void Main()
{
}
}
#endregion
Um bloco #region deve ser encerrado com a diretiva #endregion. Um #region bloco não pode se sobrepor a um bloco #if. No entanto, um bloco #region pode ser aninhado em um bloco #if e um bloco #if pode ser aninhado em um bloco #region.
Informações sobre erros e avisos
Você pode usar as seguintes diretivas para gerar erros e avisos do compilador definidos pelo usuário e controlar informações de linha:
-
#error: gere um erro do compilador com uma mensagem especificada. -
#warning: gere um aviso do compilador com uma mensagem específica. -
#line: altere o número de linha impresso com mensagens do compilador.
Use #error para gerar um erro definido pelo usuário do CS1029 de um local específico em seu código. Por exemplo:
#error Deprecated code in this method.
Observação
O compilador trata #error version de maneira especial e relata um erro do compilador, CS8304, com uma mensagem que contém o compilador usado e as versões de idioma.
Use #warning para gerar um aviso de compilador de nível um do CS1030 de um local específico em seu código. Por exemplo:
#warning Deprecated code in this method.
Use #line para modificar a numeração de linha do compilador e (opcionalmente) a saída do nome do arquivo para erros e avisos.
O exemplo a seguir mostra como relatar dois avisos associados aos números de linha. A #line 200 diretiva força o número da próxima linha a ser 200 (embora o padrão seja o nº 6) e até a próxima #line diretiva, o nome do arquivo é relatado como "Especial". A #line default diretiva retorna a numeração de linha para sua numeração padrão, que conta as linhas renumeradas pela diretiva anterior.
class MainClass
{
static void Main()
{
#line 200 "Special"
int i;
int j;
#line default
char c;
float f;
#line hidden // numbering not affected
string s;
double d;
}
}
A compilação produz a saída a seguir:
Special(200,13): warning CS0168: The variable 'i' is declared but never used
Special(201,13): warning CS0168: The variable 'j' is declared but never used
MainClass.cs(9,14): warning CS0168: The variable 'c' is declared but never used
MainClass.cs(10,15): warning CS0168: The variable 'f' is declared but never used
MainClass.cs(12,16): warning CS0168: The variable 's' is declared but never used
MainClass.cs(13,16): warning CS0168: The variable 'd' is declared but never used
A diretiva #line pode ser usada em uma etapa intermediária e automatizada no processo de build. Por exemplo, se você remover linhas do arquivo de código-fonte original, mas ainda quiser que o compilador gere a saída com base na numeração de linha original no arquivo, você poderá remover linhas e simular a numeração de linha original usando #line.
A #line hidden diretiva oculta as linhas sucessivas do depurador, de modo que, quando o desenvolvedor percorre o código, todas as linhas entre uma #line hidden e a próxima #line diretiva (supondo que não seja outra #line hidden diretiva) são pisadas. Essa opção também pode ser usada para permitir que o ASP.NET diferencie entre o código gerado pelo computador e definido pelo usuário. Embora ASP.NET seja o principal consumidor desse recurso, é provável que mais geradores de origem façam uso dele.
A diretiva #line hidden não afeta os nomes de arquivo ou números de linha nos relatórios de erro. Ou seja, se o compilador encontrar um erro em um bloco oculto, o compilador relatará o nome do arquivo atual e o número de linha do erro.
A diretiva #line filename especifica o nome de arquivo que você deseja que seja exibido na saída do compilador. Por padrão, é usado o nome real do arquivo de código-fonte. O nome do arquivo deve estar entre aspas duplas ("") e deve seguir um número de linha.
Você pode usar uma nova forma da diretiva #line:
#line (1, 1) - (5, 60) 10 "partial-class.cs"
/*34567*/int b = 0;
Os componentes dessa noda forma são:
-
(1, 1): a linha inicial e a coluna do primeiro caractere na linha seguindo a diretiva. Neste exemplo, a próxima linha é relatada como linha 1, coluna 1. -
(5, 60): a linha de extremidade e a coluna da região marcada. -
10: o deslocamento da coluna para que a diretiva#lineentre em vigor. Neste exemplo, a 10ª coluna é relatada como coluna um. A declaraçãoint b = 0;começa nessa coluna. Esse campo é opcional. Se omitida, a diretiva vai entrar em vigor na primeira coluna. -
"partial-class.cs": nome do arquivo de saída.
O exemplo anterior gera o seguinte aviso:
partial-class.cs(1,5,1,6): warning CS0219: The variable 'b' is assigned but its value is never used
Depois de remapear, a variável b está na primeira linha, no caractere seis, do arquivo partial-class.cs.
As DSLs (linguagens específicas do domínio) normalmente usam esse formato para fornecer um mapeamento melhor do arquivo de origem para a saída gerada em C#. O uso mais comum dessa diretiva de #line estendida é remapear avisos ou erros que aparecem em um arquivo gerado para a origem original. Por exemplo, considere esta página razor:
@page "/"
Time: @DateTime.NowAndThen
A propriedade DateTime.Now é digitada incorretamente como DateTime.NowAndThen. O C# gerado para o snippet razor é semelhante ao seguinte, em page.g.cs:
_builder.Add("Time: ");
#line (2, 6) - (2, 27) 15 "page.razor"
_builder.Add(DateTime.NowAndThen);
A saída do compilador para o snippet anterior é:
page.razor(2, 2, 2, 27)error CS0117: 'DateTime' does not contain a definition for 'NowAndThen'
A linha 2, coluna 6 em page.razor é onde o texto @DateTime.NowAndThen começa, anotado por (2, 6) na diretiva. Esse intervalo de @DateTime.NowAndThen termina na linha 2, coluna 27, anotada pelo (2, 27) na diretiva. O texto para DateTime.NowAndThen começa na coluna 15 de page.g.cs, conforme indicado por 15 na diretiva. O compilador relata o erro na sua localização em page.razor. O desenvolvedor pode navegar diretamente até o erro no código-fonte, não na origem gerada.
Para ver mais exemplos desse formato, confira a especificação de recurso na seção sobre exemplos.
Pragmas
O compilador usa #pragma para obter instruções especiais para compilar o arquivo em que ele aparece. O compilador deve dar suporte aos pragmas que você usa. Em outras palavras, não é possível usar #pragma para criar instruções personalizadas de pré-processamento.
-
#pragma warning: habilite ou desabilite avisos. -
#pragma checksum: gere uma soma de verificação.
#pragma pragma-name pragma-arguments
pragma-name é o nome de um pragma reconhecido.
pragma-arguments é os argumentos específicos do pragma.
aviso #pragma
O #pragma warning pode habilitar ou desabilitar determinados avisos. O #pragma warning disable format e o #pragma warning enable format controlam como o Visual Studio formata blocos de código.
#pragma warning disable warning-list
#pragma warning restore warning-list
warning-list é uma lista separada por vírgulas de números de aviso, como 414, CS3021. O prefixo "CS" é opcional. Quando você não especifica números de aviso, disable desabilita todos os avisos e restore habilita todos os avisos.
Observação
Para localizar números de aviso no Visual Studio, compile o projeto e, em seguida, procure os números de aviso na janela de Saída.
disable entra em vigor desde a próxima linha do arquivo de origem. O aviso é restaurado na linha que segue restore. Se não houver nenhum restore no arquivo, os avisos serão restaurados para o estado padrão na primeira linha de arquivos posteriores na mesma compilação.
// pragma_warning.cs
using System;
#pragma warning disable 414, CS3021
[CLSCompliant(false)]
public class C
{
int i = 1;
static void Main()
{
}
}
#pragma warning restore CS3021
[CLSCompliant(false)] // CS3021
public class D
{
int i = 1;
public static void F()
{
}
}
Outra forma do pragma warning desabilita ou restaura comandos de formatação do Visual Studio em blocos de código:
#pragma warning disable format
#pragma warning restore format
Os comandos de formato do Visual Studio não modificam o texto em blocos de código em que disable format está em vigor. Comandos de formato, como Ctrl+K, Ctrl+D, não modificam essas regiões de código. Esse pragma oferece controle fino sobre a apresentação visual do seu código.
soma de verificação #pragma
Gera somas de verificação para arquivos de origem para ajudar na depuração ASP.NET páginas.
#pragma checksum "filename" "{guid}" "checksum bytes"
A diretiva usa "filename" como o nome do arquivo para monitorar alterações ou atualizações, "{guid}" como o GUID (Identificador Global exclusivo) para o algoritmo de hash e "checksum_bytes" como a cadeia de caracteres de dígitos hexadecimal que representam os bytes da soma de verificação. Você deve fornecer um número par de dígitos hexadecimal. Um número ímpar de dígitos resulta em um aviso em tempo de compilação e a diretiva é ignorada.
O depurador do Visual Studio usa uma soma de verificação para garantir sempre a localização da fonte correta. O compilador calcula a soma de verificação para um arquivo de origem e, em seguida, emite a saída no arquivo PDB (banco de dados do programa). O depurador usa o PDB para comparar com a soma de verificação que ele calcula para o arquivo de origem.
Essa solução não funciona para projetos do ASP.NET, porque é a soma de verificação calculada para o arquivo de origem gerado e não para o arquivo .aspx. Para resolver esse problema, a #pragma checksum fornece suporte à soma de verificação para páginas do ASP.NET.
Quando você cria um projeto do ASP.NET em Visual C#, o arquivo de origem gerado contém uma soma de verificação para o arquivo .aspx, do qual a fonte é gerada. Então, o compilador grava essas informações no arquivo PDB.
Se o compilador não encontrar uma diretiva #pragma checksum no arquivo, ele calcula a soma de verificação e grava o valor no arquivo PDB.
class TestClass
{
static int Main()
{
#pragma checksum "file.cs" "{406EA660-64CF-4C82-B6F0-42D48172A799}" "ab007f1d23d9" // New checksum
}
}